- Notifications
You must be signed in to change notification settings - Fork929
feat: add session token injection to provisioner#7461
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from1 commit
478001a
49fb3b5
8b52ca5
b49d18b
3ff4539
01f7a5e
905cd1a
6ad169d
bc6dec0
1d35967
4183989
0bba543
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
- Loading branch information
Uh oh!
There was an error while loading.Please reload this page.
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -2,10 +2,12 @@ package provisionerdserver | ||
import ( | ||
"context" | ||
"crypto/sha256" | ||
"database/sql" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net" | ||
"net/http" | ||
"net/url" | ||
"reflect" | ||
@@ -37,6 +39,7 @@ import ( | ||
"github.com/coder/coder/coderd/telemetry" | ||
"github.com/coder/coder/coderd/tracing" | ||
"github.com/coder/coder/codersdk" | ||
"github.com/coder/coder/cryptorand" | ||
"github.com/coder/coder/provisioner" | ||
"github.com/coder/coder/provisionerd/proto" | ||
"github.com/coder/coder/provisionersdk" | ||
@@ -62,6 +65,7 @@ type Server struct { | ||
QuotaCommitter *atomic.Pointer[proto.QuotaCommitter] | ||
Auditor *atomic.Pointer[audit.Auditor] | ||
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore] | ||
DeploymentValues *codersdk.DeploymentValues | ||
AcquireJobDebounce time.Duration | ||
OIDCConfig httpmw.OAuth2Config | ||
@@ -193,6 +197,11 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac | ||
} | ||
} | ||
sessionToken, err := server.regenerateSessionToken(ctx, owner, workspace) | ||
if err != nil { | ||
return nil, failJob(fmt.Sprintf("regenerate session token: %s", err)) | ||
} | ||
// Compute parameters for the workspace to consume. | ||
parameters, err := parameter.Compute(ctx, server.Database, parameter.ComputeScope{ | ||
TemplateImportJobID: templateVersion.JobID, | ||
@@ -286,6 +295,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac | ||
WorkspaceOwnerId: owner.ID.String(), | ||
TemplateName: template.Name, | ||
TemplateVersion: templateVersion.Name, | ||
CoderSessionToken: sessionToken, | ||
}, | ||
LogLevel: input.LogLevel, | ||
}, | ||
@@ -1410,6 +1420,79 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. | ||
return nil | ||
} | ||
func workspaceSessionTokenName(workspace database.Workspace) string { | ||
return fmt.Sprintf("%s_%s_session_token", workspace.OwnerID, workspace.ID) | ||
mtojek marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
} | ||
// Generates a new ID and secret for an API key. | ||
// TODO put API key logic in separate package. | ||
func GenerateAPIKeyIDSecret() (id string, secret string, err error) { | ||
// Length of an API Key ID. | ||
id, err = cryptorand.String(10) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
// Length of an API Key secret. | ||
secret, err = cryptorand.String(22) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
return id, secret, nil | ||
} | ||
func (server *Server) regenerateSessionToken(ctx context.Context, user database.User, workspace database.Workspace) (string, error) { | ||
id, secret, err := GenerateAPIKeyIDSecret() | ||
if err != nil { | ||
return "", xerrors.Errorf("generate API key: %w", err) | ||
} | ||
hashed := sha256.Sum256([]byte(secret)) | ||
err = server.Database.InTx( | ||
func(tx database.Store) error { | ||
key, err := tx.GetAPIKeyByName(ctx, database.GetAPIKeyByNameParams{ | ||
UserID: workspace.OwnerID, | ||
TokenName: workspaceSessionTokenName(workspace), | ||
}) | ||
if err == nil { | ||
err = tx.DeleteAPIKeyByID(ctx, key.ID) | ||
if err != nil { | ||
return xerrors.Errorf("delete api key: %w", err) | ||
} | ||
} | ||
if err != nil && !xerrors.Is(err, sql.ErrNoRows) { | ||
return xerrors.Errorf("get api key by name: %w", err) | ||
} | ||
ip := net.IPv4(0, 0, 0, 0) | ||
bitlen := len(ip) * 8 | ||
_, err = tx.InsertAPIKey(ctx, database.InsertAPIKeyParams{ | ||
ID: id, | ||
UserID: workspace.OwnerID, | ||
LifetimeSeconds: int64(server.DeploymentValues.SessionDuration.Value().Seconds()), | ||
ExpiresAt: database.Now().Add(server.DeploymentValues.SessionDuration.Value()).UTC(), | ||
IPAddress: pqtype.Inet{ | ||
IPNet: net.IPNet{ | ||
IP: ip, | ||
Mask: net.CIDRMask(bitlen, bitlen), | ||
}, | ||
Valid: true, | ||
}, | ||
CreatedAt: database.Now(), | ||
UpdatedAt: database.Now(), | ||
HashedSecret: hashed[:], | ||
LoginType: user.LoginType, | ||
Scope: database.APIKeyScopeAll, | ||
TokenName: workspaceSessionTokenName(workspace), | ||
}) | ||
return nil | ||
}, nil) | ||
if err != nil { | ||
return "", xerrors.Errorf("regenerate API key: %w", err) | ||
} | ||
return fmt.Sprintf("%s-%s", id, secret), nil | ||
} | ||
// obtainOIDCAccessToken returns a valid OpenID Connect access token | ||
// for the user if it's able to obtain one, otherwise it returns an empty string. | ||
func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig httpmw.OAuth2Config, userID uuid.UUID) (string, error) { | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -220,6 +220,7 @@ func provisionEnv(config *proto.Provision_Config, params []*proto.ParameterValue | ||
"CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+config.Metadata.WorkspaceOwnerOidcAccessToken, | ||
"CODER_WORKSPACE_ID="+config.Metadata.WorkspaceId, | ||
"CODER_WORKSPACE_OWNER_ID="+config.Metadata.WorkspaceOwnerId, | ||
"CODER_SESSION_TOKEN="+config.Metadata.CoderSessionToken, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I doubt it, but maybe you want to confirm that we don't leak it somewhere in the provisioner logs presented in the debug mode (Try with debug mode). | ||
) | ||
for key, value := range provisionersdk.AgentScriptEnv() { | ||
env = append(env, key+"="+value) | ||
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.