Skip to content

[do not merge] Upgrade token verification to AWS SDK Go V2 #875

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
13 changes: 4 additions & 9 deletions cmd/aws-iam-authenticator/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import (
"errors"
"fmt"
"os"
"slices"
"strings"

"sigs.k8s.io/aws-iam-authenticator/pkg/arn"
"sigs.k8s.io/aws-iam-authenticator/pkg/config"
"sigs.k8s.io/aws-iam-authenticator/pkg/mapper"

"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -157,14 +158,8 @@ func getConfig() (config.Config, error) {
return cfg, errors.New("cluster ID cannot be empty")
}

partitionKeys := []string{}
partitionMap := map[string]endpoints.Partition{}
for _, p := range endpoints.DefaultPartitions() {
partitionMap[p.ID()] = p
partitionKeys = append(partitionKeys, p.ID())
}
if _, ok := partitionMap[cfg.PartitionID]; !ok {
return cfg, errors.New("Invalid partition")
if !slices.Contains(arn.PartitionKeys, cfg.PartitionID) {
return cfg, errors.New("Invalid partition when getting config")
}

// DynamicFile BackendMode and DynamicFilePath are mutually inclusive.
Expand Down
12 changes: 3 additions & 9 deletions cmd/aws-iam-authenticator/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import (

"k8s.io/sample-controller/pkg/signals"
"sigs.k8s.io/aws-iam-authenticator/pkg"
"sigs.k8s.io/aws-iam-authenticator/pkg/arn"
"sigs.k8s.io/aws-iam-authenticator/pkg/mapper"
"sigs.k8s.io/aws-iam-authenticator/pkg/metrics"
"sigs.k8s.io/aws-iam-authenticator/pkg/server"

"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -67,14 +67,8 @@ var serverCmd = &cobra.Command{
}

func init() {
partitionKeys := []string{}
for _, p := range endpoints.DefaultPartitions() {
partitionKeys = append(partitionKeys, p.ID())
}

serverCmd.Flags().String("partition",
endpoints.AwsPartitionID,
fmt.Sprintf("The AWS partition. Must be one of: %v", partitionKeys))
serverCmd.Flags().String("partition", "aws",
fmt.Sprintf("The AWS partition. Must be one of: %v", arn.PartitionKeys))
viper.BindPFlag("server.partition", serverCmd.Flags().Lookup("partition"))

serverCmd.Flags().String("generate-kubeconfig",
Expand Down
37 changes: 30 additions & 7 deletions cmd/aws-iam-authenticator/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ limitations under the License.
package main

import (
"context"
"encoding/json"
"fmt"
"os"

"sigs.k8s.io/aws-iam-authenticator/pkg/token"

"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand All @@ -54,14 +56,17 @@ var verifyCmd = &cobra.Command{
os.Exit(1)
}

sess := session.Must(session.NewSession())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we are expecting to have the session object right? But in getInstanceRegion function its best effort , any specific reason?

Copy link
Contributor Author

@gargipanatula gargipanatula Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake, this requirement shouldn't have been relaxed. I've added a panic to getInstanceRegion that occurs when config.LoadDefaultConfig returns an error, to replicate the panic that session.Must() would've sent if session.New() had an error. (session code).

ec2metadata := ec2metadata.New(sess)
instanceRegion, err := ec2metadata.Region()
ctx := context.Background()
instanceRegion := getInstanceRegion(ctx)

cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
fmt.Printf("[Warn] Region not found in instance metadata, err: %v", err)
fmt.Fprintf(os.Stderr, "unable to create sdk client configuration: %v\n", err)
os.Exit(1)
}
ec2Client := ec2.NewFromConfig(cfg)

id, err := token.NewVerifier(clusterID, partition, instanceRegion).Verify(tok)
id, err := token.NewVerifier(ctx, clusterID, partition, instanceRegion, ec2Client).Verify(tok)
if err != nil {
fmt.Fprintf(os.Stderr, "could not verify token: %v\n", err)
os.Exit(1)
Expand All @@ -79,6 +84,24 @@ var verifyCmd = &cobra.Command{
},
}

// Uses EC2 metadata to get the region. Returns "" if no region found.
func getInstanceRegion(ctx context.Context) string {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "[Warn] Unable to create config for metadata client, err: %v", err)
panic(err)
}

imdsClient := imds.NewFromConfig(cfg)
getRegionOutput, err := imdsClient.GetRegion(ctx, &imds.GetRegionInput{})
if err != nil {
fmt.Fprintf(os.Stderr, "[Warn] Region not found in instance metadata, err: %v\n", err)
return ""
}

return getRegionOutput.Region
}

func init() {
rootCmd.AddCommand(verifyCmd)
verifyCmd.Flags().StringP("token", "t", "", "Token to verify")
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.29.17
github.com/aws/aws-sdk-go-v2/credentials v1.17.70
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32
github.com/aws/aws-sdk-go-v2/service/account v1.24.2
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0
github.com/aws/smithy-go v1.22.4
Expand Down Expand Up @@ -55,7 +56,6 @@ require (
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
Expand Down
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 h1:i2vNHQiXUvKhs3quBR
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36/go.mod h1:UdyGa7Q91id/sdyHPwth+043HhmP6yP9MBHgbZM0xo8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/account v1.24.2 h1:1ItkqDExKIDsS8NoIBq7OxQOJnQNOVjC25CYa9RzOos=
github.com/aws/aws-sdk-go-v2/service/account v1.24.2/go.mod h1:NShtay87juyMTb3c6bHN6Bai5dUFmTX7NzURY4/Jyb0=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2 h1:IfMb3Ar8xEaWjgH/zeVHYD8izwJdQgRP5mKCTDt4GNk=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2/go.mod h1:35jGWx7ECvCwTsApqicFYzZ7JFEnBc6oHUuOQ3xIS54=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc=
Expand Down Expand Up @@ -86,8 +88,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down Expand Up @@ -226,7 +226,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
10 changes: 10 additions & 0 deletions hack/dev/describe-regions-policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:DescribeRegions",
"Resource": "*"
}
]
}
7 changes: 7 additions & 0 deletions hack/e2e/aws.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ function create_role() {
--assume-role-policy-document "$POLICY" \
--output text \
--query 'Role.Arn')

## attach describe-regions policy to the role
aws iam put-role-policy \
--region "${REGION}" \
--role-name "$ROLE_NAME" \
--policy-name "DescribeRegionsPolicy" \
--policy-document "file://${BASE_DIR}/../dev/describe-regions-policy.json"
else
set -e
loudecho "${ROLE_NAME} role already exists" >&2
Expand Down
11 changes: 11 additions & 0 deletions hack/e2e/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,18 @@ if [[ "${CLEAN}" == true ]]; then
"${CLUSTER_NAME}" \
"${KOPS_STATE_FILE}"

aws iam list-role-policies --role-name ${ADMIN_ROLE_NAME} --query "PolicyNames[]" --output text |
while read policy_name; do
echo "Deleting inline policy: $policy_name"
aws iam delete-role-policy --role-name ${ADMIN_ROLE_NAME} --policy-name "$policy_name"
done
aws iam delete-role --role-name "${ADMIN_ROLE_NAME}" --region ${REGION}

aws iam list-role-policies --role-name ${USER_ROLE_NAME} --query "PolicyNames[]" --output text |
while read policy_name; do
echo "Deleting inline policy: $policy_name"
aws iam delete-role-policy --role-name ${USER_ROLE_NAME} --policy-name "$policy_name"
done
aws iam delete-role --role-name "${USER_ROLE_NAME}" --region ${REGION}
else
loudecho "Not cleaning"
Expand Down
38 changes: 38 additions & 0 deletions hack/lib/dev-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ authenticator_backend_mode_dest_file="${authenticator_dynamicfile_dest_path}/bac
authenticator_config_dest_dir="/etc/authenticator"
authenticator_export_dest_dir="/var/authenticator/export"
authenticator_state_dest_dir="/var/authenticator/state"
policies_template="${REPO_ROOT}/hack/dev/policies.template"
policies_json="${OUTPUT}/dev/authenticator/policies.json"
apiserver_config_dest_dir="/etc/kubernetes/authenticator"
describe_regions_policy_json="${REPO_ROOT}/hack/dev/describe-regions-policy.json"

# Kubeconfig used when authenticator loads its mapping configuration from the API server
authenticator_kubeconfig="${authenticator_config_dest_dir}/authenticator-kubeconfig.yaml"
# Kubeconfig passed to the apiserver so it can kind its authentication webhook
Expand All @@ -86,6 +90,10 @@ kubectl_kubeconfig="${client_dir}/kubeconfig.yaml"
# Admin kubeconfig generated by kind
kind_kubeconfig="${client_dir}/kind-kubeconfig.yaml"

AWS_ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text)
DESCRIBEREGIONS_ROLE_NAME="authenticator-describeregions-role"
DESCRIBEREGIONS_POLICY_NAME="DescribeRegionsPolicy"

function install_kind() {
if ! [[ -f "${KIND_BIN}" ]]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
Expand Down Expand Up @@ -190,6 +198,33 @@ function start_authenticator_with_dynamicfile() {
chmod -R 777 "${authenticator_dynamicfile_host_path}"
chmod 777 "${authenticator_access_entry_host_file}"

# Create a role that can call ec2:DescribeRegions to run the tests
if ! RoleOutput=$(aws iam get-role --role-name "${DESCRIBEREGIONS_ROLE_NAME}" 2>&1); then
sed -e "s|{{AWS_ACCOUNT}}|${AWS_ACCOUNT}|g" \
"${policies_template}" > "${policies_json}"
sleep 2
aws iam create-role --role-name ${DESCRIBEREGIONS_ROLE_NAME} --assume-role-policy-document file://${policies_json} 1>/dev/null
echo "Waiting for IAM propagation of ${DESCRIBEREGIONS_ROLE_NAME}..."
sleep 10

aws iam put-role-policy \
--role-name $DESCRIBEREGIONS_ROLE_NAME \
--policy-name $DESCRIBEREGIONS_POLICY_NAME \
--policy-document file://$describe_regions_policy_json
sleep 2
fi

# Assume the role and get its credentials
DESCRIBEREGIONS_ROLE_ARN="arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/${DESCRIBEREGIONS_ROLE_NAME}"
ASSUME_OUTPUT=$(aws sts assume-role \
--role-arn "$DESCRIBEREGIONS_ROLE_ARN" \
--role-session-name "DescribeRegionsSession")
export AWS_ACCESS_KEY_ID=$(echo $ASSUME_OUTPUT | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $ASSUME_OUTPUT | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $ASSUME_OUTPUT | jq -r .Credentials.SessionToken)

echo "Successfully assumed role: $DESCRIBEREGIONS_ROLE_NAME"

docker run \
--detach \
--ip "${AUTHENTICATOR_IP}" \
Expand All @@ -202,6 +237,9 @@ function start_authenticator_with_dynamicfile() {
--publish ${authenticator_healthz_port}:${authenticator_healthz_port} \
--publish ${AUTHENTICATOR_PORT}:${AUTHENTICATOR_PORT} \
--env AWS_REGION="us-west-2" \
--env AWS_ACCESS_KEY_ID \
--env AWS_SECRET_ACCESS_KEY \
--env AWS_SESSION_TOKEN \
"${AUTHENTICATOR_IMAGE}" \
server \
--config "${authenticator_config_dest_dir}/authenticator_dynamicfile_mode.yaml"
Expand Down
11 changes: 11 additions & 0 deletions hack/stop-dev-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ set -o nounset
# between them is over localhost and fixed port.

REPO_ROOT="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd )"
DESCRIBEREGIONS_ROLE_NAME="authenticator-describeregions-role"

source "${REPO_ROOT}/hack/lib/dev-env.sh"

Expand All @@ -47,3 +48,13 @@ sleep 5

# Tear down network
delete_network

# Delete role used to run tests
# List inline policies
aws iam list-role-policies --role-name ${DESCRIBEREGIONS_ROLE_NAME} --query "PolicyNames[]" --output text |
while read policy_name; do
echo "Deleting inline policy: $policy_name"
aws iam delete-role-policy --role-name ${DESCRIBEREGIONS_ROLE_NAME} --policy-name "$policy_name"
done

aws iam delete-role --role-name ${DESCRIBEREGIONS_ROLE_NAME}
20 changes: 14 additions & 6 deletions pkg/arn/arn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package arn

import (
"fmt"
"slices"
"strings"

awsarn "github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go/aws/endpoints"
)

type PrincipalType int
Expand All @@ -20,6 +20,16 @@ const (
ASSUMED_ROLE
)

var PartitionKeys = []string{
"aws",
"aws-cn",
"aws-us-gov",
"aws-iso",
"aws-iso-b",
"aws-iso-e",
"aws-iso-f",
}

// Canonicalize validates IAM resources are appropriate for the authenticator
// and converts STS assumed roles into the IAM role resource.
//
Expand Down Expand Up @@ -101,10 +111,8 @@ func StripPath(arn string) (string, error) {
}

func checkPartition(partition string) error {
for _, p := range endpoints.DefaultPartitions() {
if partition == p.ID() {
return nil
}
if !slices.Contains(PartitionKeys, partition) {
return fmt.Errorf("partition %s is not recognized", partition)
}
return fmt.Errorf("partition %s is not recognized", partition)
return nil
}
1 change: 1 addition & 0 deletions pkg/ec2provider/ec2provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const (
// EC2API defines the interface for EC2 client operations
type EC2API interface {
DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
DescribeRegions(ctx context.Context, params *ec2.DescribeRegionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error)
}

// Get a node name from instance ID
Expand Down
Loading