Come accedere a un cluster kubernetes con autenticazione aws-iam-authenticator

Come accedere a un cluster kubernetes con autenticazione aws-iam-authenticator

DISCLAIMER: tutte le chiavi, i segreti e le informazioni d’accesso sono per puro scopo di spiegazione e non funzionano.

Avevo bisogno di accede ad un cluster Kubernetes (k8s) cluster e verificare le versioni, lo stato o altre varie cose dei Pod che sono dentro al cluster. Questo può essere fatto con il client kubectl che è un binario fornito dai creatori di k8s proprio. ma si può anche fare programmaticamente in Java usando un client sviluppato dal team di kubernetes

kubectl può essere usato da terminale e console e ha bisogno di qualche semplice configurazione. La mia era:

AWS ACCESS_KEY, SECRET e REGION devono essere esportati come variabili d’ambiente quindi, prima di tutto, bisogna fare una roba del genere (questo è valido per Apple. Per  Windows e Linux la sintessi è leggeremente diversa) 

export AWS_ACCESS_KEY_ID=IOP342752ADE3456156
export AWS_SECRET_ACCESS_KEY=KN!/kFssdfFWewcASF12dasWE365574FFo
export AWS_DEFAULT_REGION=eu-west-1

Dopodichè c’è un file di configurazione, e del quale esportato il path

export KUBECONFIG=/home/mysuer/.kubenv

Nel file .kubenv c’è della roba del genere  (il formato del file è YML)

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: 4oCU4oCTQkVHSU4gQ0VSVElGSUNBVEXigJTigJMKTUlJQy9qQ0NsMmdBd0lCQWdJTEJBQUFBQUFCRlV0YXc1UXdEUVlKS29aSWh2Y05BUUVGQlFBd1Z6RUxNQWtHCllXeFRhV2R1SUc1MkxYTmhNUkF3RGdZRFZRUUxFd2RTYjI5MElFTkJNUnN3R1FZRFZRUURFeEpIYkc5aVlXeFQKYVdkdUlGSnZiM1FnUTBFd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURhRHVhWgpqYzZqNDArS2Z2dnhpNE1sYStwSUgvRXFzTG1WRVFTOThHUFI0bWRtenh6ZHp4dElLKzZOaVk2YXJ5bUFaYXZwCjM4TmZsTlVWeVJSQm5NUmRkV1FWRGY5Vk1PeUdqLzhON3l5NVkwYjJxdnpmdkduOUxoSklaSnJnbGZDbTd5bVAKSE1VZnBJQnZGU0RKM2d5SUNoM1dabFhpL0VqSktTWnA0QT09CuKAlOKAk0VORCBDRVJUSUZJQ0FUReKAlOKAkw==
    server: https://ABCDEFGHILMNOPQRSTUVZ1234567890.gr7.eu-west-1.eks.amazonaws.com
  name: arn:aws:eks:eu-west-1:111111111111:cluster/a-k8s-cluster-01
contexts:
- context:
    cluster: arn:aws:eks:eu-west-1:111111111111:cluster/a-k8s-cluster-01
    user: arn:aws:eks:eu-west-1:111111111111:cluster/a-k8s-cluster-01
  name: arn:aws:eks:eu-west-1:111111111111:cluster/a-k8s-cluster-01
current-context: arn:aws:eks:eu-west-1:111111111111:cluster/a-k8s-cluster-01
kind: Config
preferences: {}
users:
- name: arn:aws:eks:eu-west-1:111111111111:cluster/a-k8s-cluster-01
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - token
      - -i
      - a-k8s-cluster-01
      command: /var/bin/aws-iam-authenticator
      env:
      - name: "AWS_PROFILE"
        value: "this-is-a-k8s-clusters"
      interactiveMode: IfAvailable
      provideClusterInfo: false

 

Dopo l’export si può usare il comando per prendere tutti i  namespace dal cluster Kubernetes: 

kubectl get namespaces

E per ogni namespace, si prendono le informazioni dei PODS

kubectl get deployments -o wide -n A_NAMESPACE

sostituendo A_NAMESPACE con i vari namespace del precedente comando.

Come autenticarsi e prendere le informazioni sui POD da un cluster k8s (kubernetes) in Java

Per fare la stessa cosa in Java, si usa un Java client e per prima cosa bisogna autenticarsi su k8s (Kubernetes) sequendo i seguenti passi:

Esportare AWS secret, access key e region 

Questa parte non può essere skippata, quindi anche in Java dobbiamo esportare queste variabili prima di far girare il codice

export AWS_ACCESS_KEY_ID=IOP342752ADE3456156
export AWS_SECRET_ACCESS_KEY=KN!/kFssdfFWewcASF12dasWE365574FFo
export AWS_DEFAULT_REGION=eu-west-1

Dopodichè, seguendo questo esempio Code Example for kubernetes Java client , la prima cosa da fare è l’autenticazione. A tal riguardo ho bisogno di un poco di modifiche

 

Region myRegion = Regions.EU_WEST_1;
String clusterName = "a-k8s-cluster-01";
String clusterHost = "https://ABCDEFGHILMNOPQRSTUVZ1234567890.gr7.";
String certificate = "4oCU4oCTQkVHSU4gQ0VSVElGSUNBVEXigJTigJMKTUlJQy9qQ0NsMmdBd0lCQWdJTEJBQUFBQUFCRlV0YXc1UXdEUVlKS29aSWh2Y05BUUVGQlFBd1Z6RUxNQWtHCllXeFRhV2R1SUc1MkxYTmhNUkF3RGdZRFZRUUxFd2RTYjI5MElFTkJNUnN3R1FZRFZRUURFeEpIYkc5aVlXeFQKYVdkdUlGSnZiM1FnUTBFd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURhRHVhWgpqYzZqNDArS2Z2dnhpNE1sYStwSUgvRXFzTG1WRVFTOThHUFI0bWRtenh6ZHp4dElLKzZOaVk2YXJ5bUFaYXZwCjM4TmZsTlVWeVJSQm5NUmRkV1FWRGY5Vk1PeUdqLzhON3l5NVkwYjJxdnpmdkduOUxoSklaSnJnbGZDbTd5bVAKSE1VZnBJQnZGU0RKM2d5SUNoM1dabFhpL0VqSktTWnA0QT09CuKAlOKAk0VORCBDRVJUSUZJQ0FUReKAlOKAkw==";

AWSCredentialsProvider credentialsProvider = new DefaultAWSCredentialsProviderChain();
AWSSecurityTokenServiceClient tokenService = (AWSSecurityTokenServiceClient) AWSSecurityTokenServiceClientBuilder
        .standard()
        .withCredentials(credentialsProvider)
        .withRegion(myRegion)
        .build();
String initialToken = generateToken(clusterName,
        new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(60)),
        "sts",
        myRegion.getName(),
        tokenService,
        credentialsProvider,
        "https",
        "sts." + myRegion.getName() + ".amazonaws.com");

byte[] bytes = Base64.getDecoder().decode(certificate.getBytes());
String endPoint = clusterHost + myRegion.getName()
        + ".eks.amazonaws.com";
client = (new ClientBuilder())
        .setBasePath(endPoint)
        .setAuthentication(new AccessTokenAuthentication(initialToken))
        .setVerifyingSsl(true)
        .setCertificateAuthority(bytes)
        .build();

Configuration.setDefaultApiClient(client);

CoreV1Api api = new CoreV1Api();
V1NamespaceList namespaces = api.listNamespace(null, null, null, null, null, -1, null, null, 30, false);
...
..
.

Dopo di questo, avendo i namespace e le api instanziate, il tutto è piuttosto semplice. Prima di analizzare la soluzione, lasciatemi aggiungere lo snippet del codice di generateToken che è un copy/paste da questo thread e siccome funziona, io non investigheri più di tanto Occhi sorridenti

private String generateToken(String clusterName,
                             Date expirationDate,
                             String serviceName,
                             String region,
                             AWSSecurityTokenServiceClient awsSecurityTokenServiceClient,
                             AWSCredentialsProvider credentialsProvider,
                             String scheme,
                             String host) throws URISyntaxException {
    try {
        DefaultRequest<GetCallerIdentityRequest> callerIdentityRequestDefaultRequest = new DefaultRequest<>(new GetCallerIdentityRequest(), serviceName);
        URI uri = new URI(scheme, host, null, null);
        callerIdentityRequestDefaultRequest.setResourcePath("/");
        callerIdentityRequestDefaultRequest.setEndpoint(uri);
        callerIdentityRequestDefaultRequest.setHttpMethod(HttpMethodName.GET);
        callerIdentityRequestDefaultRequest.addParameter("Action", "GetCallerIdentity");
        callerIdentityRequestDefaultRequest.addParameter("Version", "2011-06-15");
        callerIdentityRequestDefaultRequest.addHeader("x-k8s-aws-id", clusterName);

        Signer signer = SignerFactory.createSigner(SignerFactory.VERSION_FOUR_SIGNER, new SignerParams(serviceName, region));
        SignerProvider signerProvider = new DefaultSignerProvider(awsSecurityTokenServiceClient, signer);
        PresignerParams presignerParams = new PresignerParams(uri,
                credentialsProvider,
                signerProvider,
                SdkClock.STANDARD);

        PresignerFacade presignerFacade = new PresignerFacade(presignerParams);
        URL url = presignerFacade.presign(callerIdentityRequestDefaultRequest, expirationDate);
        String encodedUrl = Base64.getUrlEncoder().withoutPadding().encodeToString(url.toString().getBytes());
        log.info("Token [{}]", encodedUrl);
        return "k8s-aws-v1." + encodedUrl;
    } catch (URISyntaxException e) {
        log.error("could not generate token", e);
        throw e;
    }
}

La parte principale del codice è “come ci siamo spostati da un file di configurazione .kubenv a del codice Java”. Le seguenti variabili

Region myRegion = Regions.EU_WEST_1;
String clusterName = "a-k8s-cluster-01";
String clusterHost = "https://ABCDEFGHILMNOPQRSTUVZ1234567890.gr7.";
String certificate = "4oCU4oCTQkVHSU4gQ0VSVElGSUNBVEXigJTigJMKTUlJQy9qQ0NsMmdBd0lCQWdJTEJBQUFBQUFCRlV0YXc1UXdEUVlKS29aSWh2Y05BUUVGQlFBd1Z6RUxNQWtHCllXeFRhV2R1SUc1MkxYTmhNUkF3RGdZRFZRUUxFd2RTYjI5MElFTkJNUnN3R1FZRFZRUURFeEpIYkc5aVlXeFQKYVdkdUlGSnZiM1FnUTBFd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURhRHVhWgpqYzZqNDArS2Z2dnhpNE1sYStwSUgvRXFzTG1WRVFTOThHUFI0bWRtenh6ZHp4dElLKzZOaVk2YXJ5bUFaYXZwCjM4TmZsTlVWeVJSQm5NUmRkV1FWRGY5Vk1PeUdqLzhON3l5NVkwYjJxdnpmdkduOUxoSklaSnJnbGZDbTd5bVAKSE1VZnBJQnZGU0RKM2d5SUNoM1dabFhpL0VqSktTWnA0QT09CuKAlOKAk0VORCBDRVJUSUZJQ0FUReKAlOKAkw==";

Sono semplici da spiegare:

myRegion: è la regione AWS e si trova nel file di configurazione .kuebenv, in ogni server o host dovrebbe esserci qualcosa come “eu-west-1”

clusterName: è il nome del cluster, potresti trovarlo nel file di configurazione .kuebenv come parte finale dei nodi clusters -> cluster -> name oppure nel campo users -> user -> exec -> args sempre del file .kuebenv e viene utilizzato come argomento quando chiamiamo il file binario aws-iam-authenticator

clusterHost: nel file di configurazione di .kuebenv è il campo cluster -> cluster -> server

certificate: nel file di configurazione di .kuebenv è il campo cluster -> certificate-authority-data. Questo rappresenta un certificato codificato Base64, se lo decodifichi con un semplice decoder Base6e, quello che esce fuori è il seguente testo

—–BEGIN CERTIFICATE—–
MIIC/jCCl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
—–END CERTIFICATE—–

Quindi tutto dovrebbere funzionare e dovresti avere accesso a tutte le  API in Java.

Riferimenti:

Need aws-iam-authenticator thread

Code Example for kubernetes Java client

MiroAdmin