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
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: