chore: move app proxying code to workspaceapps pkg (#6998)

* chore: move app proxying code to workspaceapps pkg

Moves path-app, subdomain-app and reconnecting PTY proxying to the new
workspaceapps.WorkspaceAppServer struct. This is in preparation for
external workspace proxies.

Updates app logout flow to avoid redirecting to coder-logout.${app_host}
on logout. Instead, all subdomain app tokens owned by the logging-out
user will be deleted every time you logout for simplicity sake.

Tests will remain in their original package, pending being moved to an
apptest package (or similar).

Co-authored-by: Steven Masley <stevenmasley@coder.com>
This commit is contained in:
Dean Sheather
2023-04-06 04:41:55 +10:00
committed by GitHub
parent 0069831e8d
commit eb66cc9f35
28 changed files with 1236 additions and 1334 deletions

View File

@ -78,6 +78,7 @@ import (
"github.com/coder/coder/coderd/tracing"
"github.com/coder/coder/coderd/updatecheck"
"github.com/coder/coder/coderd/util/slice"
"github.com/coder/coder/coderd/workspaceapps"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/cryptorand"
"github.com/coder/coder/provisioner/echo"
@ -781,37 +782,42 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
}
}
// Read the app signing key from the DB. We store it hex
// encoded since the config table uses strings for the value and
// we don't want to deal with automatic encoding issues.
appSigningKeyStr, err := tx.GetAppSigningKey(ctx)
// Read the app signing key from the DB. We store it hex encoded
// since the config table uses strings for the value and we
// don't want to deal with automatic encoding issues.
appSecurityKeyStr, err := tx.GetAppSecurityKey(ctx)
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
return xerrors.Errorf("get app signing key: %w", err)
}
if appSigningKeyStr == "" {
// Generate 64 byte secure random string.
b := make([]byte, 64)
// If the string in the DB is an invalid hex string or the
// length is not equal to the current key length, generate a new
// one.
//
// If the key is regenerated, old signed tokens and encrypted
// strings will become invalid. New signed app tokens will be
// generated automatically on failure. Any workspace app token
// smuggling operations in progress may fail, although with a
// helpful error.
if decoded, err := hex.DecodeString(appSecurityKeyStr); err != nil || len(decoded) != len(workspaceapps.SecurityKey{}) {
b := make([]byte, len(workspaceapps.SecurityKey{}))
_, err := rand.Read(b)
if err != nil {
return xerrors.Errorf("generate fresh app signing key: %w", err)
}
appSigningKeyStr = hex.EncodeToString(b)
err = tx.InsertAppSigningKey(ctx, appSigningKeyStr)
appSecurityKeyStr = hex.EncodeToString(b)
err = tx.UpsertAppSecurityKey(ctx, appSecurityKeyStr)
if err != nil {
return xerrors.Errorf("insert freshly generated app signing key to database: %w", err)
}
}
appSigningKey, err := hex.DecodeString(appSigningKeyStr)
appSecurityKey, err := workspaceapps.KeyFromString(appSecurityKeyStr)
if err != nil {
return xerrors.Errorf("decode app signing key from database as hex: %w", err)
}
if len(appSigningKey) != 64 {
return xerrors.Errorf("app signing key must be 64 bytes, key in database is %d bytes", len(appSigningKey))
return xerrors.Errorf("decode app signing key from database: %w", err)
}
options.AppSigningKey = appSigningKey
options.AppSecurityKey = appSecurityKey
return nil
}, nil)
if err != nil {

View File

@ -668,8 +668,7 @@ func TestServer(t *testing.T) {
if c.tlsListener {
accessURLParsed, err := url.Parse(c.requestURL)
require.NoError(t, err)
client := codersdk.New(accessURLParsed)
client.HTTPClient = &http.Client{
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
@ -682,11 +681,15 @@ func TestServer(t *testing.T) {
},
},
}
defer client.HTTPClient.CloseIdleConnections()
_, err = client.HasFirstUser(ctx)
if err != nil {
require.ErrorContains(t, err, "Invalid application URL")
}
defer client.CloseIdleConnections()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, accessURLParsed.String(), nil)
require.NoError(t, err)
resp, err := client.Do(req)
// We don't care much about the response, just that TLS
// worked.
require.NoError(t, err)
defer resp.Body.Close()
}
})
}