awslabs / aws-sdk-rust

AWS SDK for the Rust Programming Language
https://awslabs.github.io/aws-sdk-rust/
Apache License 2.0
2.98k stars 246 forks source link

Missing EKS get-token #980

Closed subtil-next closed 10 months ago

subtil-next commented 10 months ago

Describe the feature

the aws_sdk_eks crate appears to be missing the get-token call.

https://docs.aws.amazon.com/cli/latest/reference/eks/get-token.html

Use Case

Get-Token is needed to auth into EKS clusters: https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html

Proposed Solution

No response

Other Information

No response

Acknowledgements

A note for the community

Community Note

rcoh commented 10 months ago

I think that's a custom function exclusive to the CLI. Code here: https://github.com/aws/aws-cli/blob/00a26836b824deb9a07c8b9d06ddfddec084cafc/awscli/customizations/eks/get_token.py#L222

subtil-next commented 10 months ago

Oh. Awesome. Thanks!

github-actions[bot] commented 10 months ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

subtil-next commented 9 months ago

If anyone comes across this and is looking for the implementation:

pub fn generate_token<'a>(
    region: &Region,
    headers: impl Iterator<Item = (&'a str, &'a str)>,
    credentials: &Credentials,
    duration: Option<Duration>,
    time: Option<SystemTime>,
) -> Result<String, SigningError> {
    let expiration = credentials.expiry();
    let region = region.to_string();
    let identity = Identity::new(credentials.clone(), expiration);
    let mut signing_settings = SigningSettings::default();
    signing_settings.signature_location = SignatureLocation::QueryParams;
    signing_settings.expires_in = duration.or(Some(Duration::from_secs(60))); // 1 minute

    let signing_params = aws_sigv4::sign::v4::SigningParams::builder()
        .identity(&identity)
        .region(&region)
        .name("sts")
        .time(time.unwrap_or(SystemTime::now()))
        .settings(signing_settings)
        .build()
        .unwrap();

    // Convert the HTTP request into a signable request
    let url =
        format!("https://sts.{region}.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15");
    let signable_request =
        SignableRequest::new("GET", url.clone(), headers, SignableBody::Bytes(&[]))
            .expect("signable request");

    let (signing_instructions, _signature) = aws_sigv4::http_request::sign(
        signable_request,
        &aws_sigv4::http_request::SigningParams::V4(signing_params),
    )?
    .into_parts();
    let mut my_req = Request::builder().uri(url).body(()).unwrap();

    signing_instructions.apply_to_request_http0x(&mut my_req);
    let uri = my_req.uri().to_string();

    Ok(format!("k8s-aws-v1.{}", encode(uri)))
}
MushiTheMoshi commented 7 months ago

If anyone comes across this and is looking for the implementation:

pub fn generate_token<'a>(
    region: &Region,
    headers: impl Iterator<Item = (&'a str, &'a str)>,
    credentials: &Credentials,
    duration: Option<Duration>,
    time: Option<SystemTime>,
) -> Result<String, SigningError> {
    let expiration = credentials.expiry();
    let region = region.to_string();
    let identity = Identity::new(credentials.clone(), expiration);
    let mut signing_settings = SigningSettings::default();
    signing_settings.signature_location = SignatureLocation::QueryParams;
    signing_settings.expires_in = duration.or(Some(Duration::from_secs(60))); // 1 minute

    let signing_params = aws_sigv4::sign::v4::SigningParams::builder()
        .identity(&identity)
        .region(&region)
        .name("sts")
        .time(time.unwrap_or(SystemTime::now()))
        .settings(signing_settings)
        .build()
        .unwrap();

    // Convert the HTTP request into a signable request
    let url =
        format!("https://sts.{region}.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15");
    let signable_request =
        SignableRequest::new("GET", url.clone(), headers, SignableBody::Bytes(&[]))
            .expect("signable request");

    let (signing_instructions, _signature) = aws_sigv4::http_request::sign(
        signable_request,
        &aws_sigv4::http_request::SigningParams::V4(signing_params),
    )?
    .into_parts();
    let mut my_req = Request::builder().uri(url).body(()).unwrap();

    signing_instructions.apply_to_request_http0x(&mut my_req);
    let uri = my_req.uri().to_string();

    Ok(format!("k8s-aws-v1.{}", encode(uri)))
}

Any idea how it should look like next part where you actually use this token?

subtil-next commented 7 months ago

I use:

kube = "0.88.0"
k8s-openapi = { version = "0.21.0", features = ["latest", "v1_29"] }
pem = "3.0.3"

with something like:

use kube::{Client, Config};

 let mut config = Config::new(uri);

// URL_SAFE base64 decode the certificate
    let decoded = match decode(certificate) {
        Ok(s) => s,
        Err(d) => {
            warn!(
                "Failed to decode certificate. Unable to pull kubernetes information {:?}",
                d
            );
            return None;
        }
    };
// parse the certificates. 
    let decoded = pem::parse_many(decoded)
        .unwrap()
        .into_iter()
        .filter_map(|p| {
            if p.tag() == "CERTIFICATE" {
                Some(p.into_contents())
            } else {
                None
            }
        })
        .collect::<Vec<_>>();
    config.root_cert = Some(decoded);
    config.auth_info.token = Some(SecretString::new(token));

let client =  Client::try_from(config).expect("failed to create kube client")
MushiTheMoshi commented 7 months ago

thank you so much for your quick response!