mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
feat: Add AWS instance identity authentication (#570)
* feat: Add AWS instance identity authentication This allows zero-trust authentication for all AWS instances. Prior to this, AWS instances could be used by passing `CODER_TOKEN` as an environment variable to the startup script. AWS explicitly states that secrets should not be passed in startup scripts because it's user-readable. * Fix sha256 verbosity * Fix HTTP client being exposed on auth
This commit is contained in:
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/go-chi/render"
|
||||
|
||||
"github.com/coder/coder/coderd/awsidentity"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/httpapi"
|
||||
"github.com/coder/coder/codersdk"
|
||||
@ -16,6 +17,24 @@ import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// AWS supports instance identity verification:
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
func (api *api) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
var req codersdk.AWSInstanceIdentityToken
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
return
|
||||
}
|
||||
identity, err := awsidentity.Validate(req.Signature, req.Document, api.AWSCertificates)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
|
||||
Message: fmt.Sprintf("validate: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
api.handleAuthInstanceID(rw, r, identity.InstanceID)
|
||||
}
|
||||
|
||||
// Google Compute Engine supports instance identity verification:
|
||||
// https://cloud.google.com/compute/docs/instances/verifying-instance-identity
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
@ -47,10 +66,14 @@ func (api *api) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter,
|
||||
})
|
||||
return
|
||||
}
|
||||
agent, err := api.Database.GetWorkspaceAgentByInstanceID(r.Context(), claims.Google.ComputeEngine.InstanceID)
|
||||
api.handleAuthInstanceID(rw, r, claims.Google.ComputeEngine.InstanceID)
|
||||
}
|
||||
|
||||
func (api *api) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, instanceID string) {
|
||||
agent, err := api.Database.GetWorkspaceAgentByInstanceID(r.Context(), instanceID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
|
||||
Message: fmt.Sprintf("instance with id %q not found", claims.Google.ComputeEngine.InstanceID),
|
||||
Message: fmt.Sprintf("instance with id %q not found", instanceID),
|
||||
})
|
||||
return
|
||||
}
|
||||
@ -107,7 +130,7 @@ func (api *api) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter,
|
||||
}
|
||||
if latestHistory.ID.String() != resourceHistory.ID.String() {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: fmt.Sprintf("resource found for id %q, but isn't registered on the latest history", claims.Google.ComputeEngine.InstanceID),
|
||||
Message: fmt.Sprintf("resource found for id %q, but isn't registered on the latest history", instanceID),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user