Skip to content
Open
Changes from all commits
Commits
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
51 changes: 47 additions & 4 deletions hatchery/sharedworkspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package hatchery

import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
Expand All @@ -14,6 +16,7 @@ import (
"github.com/aws/aws-sdk-go/service/iam"
k8sv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8svalidation "k8s.io/apimachinery/pkg/util/validation"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)

Expand All @@ -24,9 +27,22 @@ func sharedWorkspaceSAName(userName string) string {
return fmt.Sprintf("hatchery-shared-%s", escapism(userName))
}

// shortenedWorkspaceRoleName returns the workspace role name hashed version.
func shortenedWorkspaceRoleName(prefix string, namespace string, name string) string {
suffix := fmt.Sprintf("%s-%s", escapism(namespace), escapism(name))
sum := sha256.Sum256([]byte(suffix))
hash := hex.EncodeToString(sum[:])[:16]
out := fmt.Sprintf("%s-%s", prefix, hash)
return out
}

// sharedWorkspaceRoleName returns the per-user AWS IAM role name.
func sharedWorkspaceRoleName(userName string) string {
return fmt.Sprintf("hatchery-shared-%s", escapism(userName))
func sharedWorkspaceRoleName(namespace string, userName string) string {
old := fmt.Sprintf("hatchery-shared-%s-%s", escapism(namespace), escapism(userName))
if len(old) < 64 {
return old
}
return shortenedWorkspaceRoleName("hatchery-shared", namespace, userName)
}

// oidcProviderID returns the host portion of the OIDC provider ARN,
Expand All @@ -53,7 +69,7 @@ func accountIDFromOIDCARN(providerARN string) string {
// per-user IAM role whose S3 policy is scoped to exactly the given prefixes.
// Returns the role ARN.
func ensureSharedWorkspaceIAMRole(userName, namespace, oidcProviderARN string, prefixes []SharedWorkspacePrefix) (string, error) {
roleName := sharedWorkspaceRoleName(userName)
roleName := sharedWorkspaceRoleName(namespace, userName)
accountID := accountIDFromOIDCARN(oidcProviderARN)
providerID := oidcProviderID(oidcProviderARN)
saName := sharedWorkspaceSAName(userName)
Expand Down Expand Up @@ -111,6 +127,16 @@ func ensureSharedWorkspaceIAMRole(userName, namespace, oidcProviderARN string, p
if _, err = svc.CreateRole(&iam.CreateRoleInput{
RoleName: aws.String(roleName),
AssumeRolePolicyDocument: aws.String(trustPolicy),
Tags: []*iam.Tag{
{
Key: aws.String("Namespace"),
Value: &namespace,
},
{
Key: aws.String("Username"),
Value: &userName,
},
},
}); err != nil {
return "", fmt.Errorf("failed to create IAM role %s: %w", roleName, err)
}
Expand Down Expand Up @@ -190,6 +216,23 @@ func sharedPVCName(userName, prefixName string) string {
return fmt.Sprintf("shared-claim-%s-%s", escapism(userName), escapism(prefixName))
}

// shortenedVolumeName returns the volume name hashed version.
func shortenedVolumeName(prefix string, name string) string {
sum := sha256.Sum256([]byte(name))
hash := hex.EncodeToString(sum[:])[:16]
out := fmt.Sprintf("%s-%s", prefix, hash)
return out
}

// sharedVolName returns the volume name or a hashed version.
func sharedVolName(name string) string {
old := fmt.Sprintf("shared-%s", escapism(name))
if len(k8svalidation.IsDNS1123Label(old)) == 0 {
return old
}
return shortenedVolumeName("shared", name)
}

// getSharedWorkspacePrefixes calls the configured external API with the user's
// bearer token and returns the list of S3 prefixes the user may access.
func getSharedWorkspacePrefixes(ctx context.Context, accessToken string) ([]SharedWorkspacePrefix, error) {
Expand Down Expand Up @@ -333,7 +376,7 @@ func addSharedWorkspaceVolumesToPod(pod *k8sv1.Pod, userName string, prefixes []
}
for _, prefix := range prefixes {
pvcName := sharedPVCName(userName, prefix.Name)
volName := fmt.Sprintf("shared-%s", escapism(prefix.Name))
volName := sharedVolName(prefix.Name)
relativePrefixPath := strings.Trim(prefix.Prefix, "/")
if relativePrefixPath == "" {
relativePrefixPath = prefix.Name
Expand Down
Loading