<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcelo Andrade</title>
    <description>The latest articles on DEV Community by Marcelo Andrade (@marcelo_devsres).</description>
    <link>https://dev.to/marcelo_devsres</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F668646%2Fd4751fd6-1f27-4557-a3ae-aa88961bc50e.png</url>
      <title>DEV Community: Marcelo Andrade</title>
      <link>https://dev.to/marcelo_devsres</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcelo_devsres"/>
    <language>en</language>
    <item>
      <title>Entendendo Kubernetes Ingress Controllers e as vulnerabilidades recentemente anunciadas do Nginx</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Mon, 31 Mar 2025 08:34:16 +0000</pubDate>
      <link>https://dev.to/aws-builders/entendendo-kubernetes-ingress-controllers-e-as-vulnerabilidades-recentemente-anunciadas-do-nginx-7km</link>
      <guid>https://dev.to/aws-builders/entendendo-kubernetes-ingress-controllers-e-as-vulnerabilidades-recentemente-anunciadas-do-nginx-7km</guid>
      <description>&lt;p&gt;Recentemente, &lt;a href="https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.12.1" rel="noopener noreferrer"&gt;uma série de vulnerabilidades&lt;/a&gt; foi anunciada para o &lt;a href="https://kubernetes.github.io/ingress-nginx/" rel="noopener noreferrer"&gt;Nginx Ingress Controller&lt;/a&gt;, algo bem preocupante, levando em consideração que este é o Ingress Controller "padrão" para a maioria das pessoas que estão começando com Kubernetes. &lt;a href="https://kubernetes.io/blog/2025/03/24/ingress-nginx-cve-2025-1974/" rel="noopener noreferrer"&gt;Uma delas, em particular&lt;/a&gt;, recebeu um score de &lt;a href="https://www.first.org/cvss/calculator/3-1#CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" rel="noopener noreferrer"&gt;&lt;strong&gt;9.8&lt;/strong&gt;&lt;/a&gt;, pois não exige basicamente nenhum privilégio especial, bastando o usuário ter conectividade com o componente vulnerável.&lt;/p&gt;

&lt;p&gt;Fui torpedeado com várias dúvidas a respeito essa semana no trabalho e fora dele a respeito, e acredito que vale a pena compartilhar algumas delas por aqui.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é &lt;strong&gt;Ingress&lt;/strong&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" rel="noopener noreferrer"&gt;Ingress&lt;/a&gt; é um tipo de recurso do Kubernetes que permite descrever como sua aplicação pode ser acessada usando definições de protocolo de alto nível ("camada 7").&lt;/p&gt;

&lt;p&gt;Diferentemente dos &lt;strong&gt;Services&lt;/strong&gt; do tipo &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer" rel="noopener noreferrer"&gt;LoadBalancer&lt;/a&gt;, que configuram um balanceador de carga camada 4 para encaminhar todo tráfego recebido para um conjunto de &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/" rel="noopener noreferrer"&gt;&lt;strong&gt;Pods&lt;/strong&gt;&lt;/a&gt; normalmente associados a um único &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" rel="noopener noreferrer"&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/a&gt;, o &lt;strong&gt;Ingress&lt;/strong&gt; permite que várias aplicações diferentes sejam servidas a partir de um mesmo domínio ou balanceador:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subs-devsres-com&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subs.devsres.com"&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/admin"&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin-module&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/assets"&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assets&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/esskay"&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uwu&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As requisições ao domínio &lt;strong&gt;subs.devsres.com&lt;/strong&gt; serão encaminhadas para os módulos da minha aplicação &lt;strong&gt;admin&lt;/strong&gt;, &lt;strong&gt;assets&lt;/strong&gt; ou &lt;strong&gt;esskay&lt;/strong&gt;, dependendo do &lt;strong&gt;path&lt;/strong&gt; acessado na URL.&lt;/p&gt;

&lt;p&gt;Eu descrevo &lt;a href="https://www.instagram.com/p/CV76ZWFrz3i/?img_index=8" rel="noopener noreferrer"&gt;neste post&lt;/a&gt; como usar &lt;strong&gt;Ingress&lt;/strong&gt; pode ser o que viabiliza um projeto por minimizar o número de balanceadores instanciados em uma nuvem pública.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é um &lt;strong&gt;Ingress Controller&lt;/strong&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/" rel="noopener noreferrer"&gt;&lt;strong&gt;Controllers&lt;/strong&gt;&lt;/a&gt; são a base do funcionamento do Kubernetes, e correspondem a programas que executam de maneira contínua para garantir que o que você descreve em seus &lt;strong&gt;arquivos yaml&lt;/strong&gt; sejam criados e continuem existindo.&lt;/p&gt;

&lt;p&gt;Controllers instanciam seus &lt;strong&gt;Pods&lt;/strong&gt; ao criar um &lt;strong&gt;Deployment&lt;/strong&gt;, e garantem que iniciarão novamente caso a aplicação dê um &lt;em&gt;crash&lt;/em&gt;, ou sejam recriados caso alguma máquina do cluster Kubernetes falhe.&lt;/p&gt;

&lt;p&gt;Um &lt;strong&gt;Ingress Controller&lt;/strong&gt; é um programa que lê os objetos &lt;strong&gt;Ingress&lt;/strong&gt; do cluster e viabiliza sua implementação em algum lugar.&lt;/p&gt;

&lt;p&gt;A descrição acima ("em algum lugar") é bastante genérica porque, diferentemente da grande maioria dos outros recursos nativos, como &lt;strong&gt;Pods&lt;/strong&gt;, &lt;strong&gt;Services&lt;/strong&gt; ou &lt;strong&gt;Deployments&lt;/strong&gt;, clusters Kubernetes &lt;strong&gt;não vêm&lt;/strong&gt; com &lt;strong&gt;Controllers&lt;/strong&gt; para o objeto &lt;strong&gt;Ingress&lt;/strong&gt; na sua instalação padrão. E isso é algo mais comum do que parece: &lt;strong&gt;Services&lt;/strong&gt; do tipo &lt;strong&gt;LoadBalancer&lt;/strong&gt; também podem não funcionar, dependendo de onde seu cluster é criado; &lt;strong&gt;NetworkPolicies&lt;/strong&gt; dependem do plugin CNI escolhido e podem não funcionar; &lt;strong&gt;PersistentVolumes&lt;/strong&gt; também precisam de configuração de componentes externos... Isso contribui com a percebida complexidade de uso do Kubernetes, que nem mesmo soluções gerenciadas de nuvem conseguem apaziguar.&lt;/p&gt;

&lt;p&gt;A pior parte: muitos dos componentes sem &lt;strong&gt;Controllers&lt;/strong&gt; nativos &lt;strong&gt;não são&lt;/strong&gt; "opcionais" para a maioria dos usos da tecnologia! &lt;/p&gt;

&lt;h2&gt;
  
  
  O que é o &lt;strong&gt;Ingress Nginx Controller&lt;/strong&gt;?
&lt;/h2&gt;

&lt;p&gt;O projeto Kubernetes mantém três subprojetos de &lt;strong&gt;Ingress&lt;/strong&gt; sob sua guarda; um deles é o &lt;a href="https://kubernetes.github.io/ingress-nginx/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ingress Nginx Controller&lt;/strong&gt;&lt;/a&gt;, e os demais são específicos para clouds públicas (&lt;a href="https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt; e &lt;a href="https://github.com/kubernetes/ingress-gce" rel="noopener noreferrer"&gt;GCP&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Os dois &lt;strong&gt;Ingress Controllers&lt;/strong&gt; de nuvens públicas operam como o esperado, traduzindo o que é descrito nos &lt;strong&gt;yamls&lt;/strong&gt; de &lt;strong&gt;Ingress&lt;/strong&gt; para configurações das ofertas de balanceadores de carga.  &lt;/p&gt;

&lt;p&gt;Já o &lt;strong&gt;Ingress Nginx Controller&lt;/strong&gt; executa um &lt;strong&gt;Nginx&lt;/strong&gt; como proxy reverso e gera &lt;strong&gt;configurações&lt;/strong&gt; para este Nginx a partir dos seus objetos &lt;strong&gt;Ingress&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Embora a documentação liste &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/" rel="noopener noreferrer"&gt;uns trinta controllers diferentes&lt;/a&gt;, é bastante comum pensar que o &lt;strong&gt;Ingress Nginx Controller&lt;/strong&gt; é a principal oferta disponível para Kubernetes fora das principais nuvens, e sua ampla adoção reflete este pensamento.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ingress NGINX Controller ou NGINX Ingress Controller?
&lt;/h2&gt;

&lt;p&gt;Parece piada, mas não é. Mas o &lt;a href="https://github.com/kubernetes/ingress-nginx" rel="noopener noreferrer"&gt;Ingress NGINX Controller&lt;/a&gt; não tem &lt;strong&gt;nada em comum&lt;/strong&gt; com o &lt;a href="https://github.com/nginx/kubernetes-ingress" rel="noopener noreferrer"&gt;NGINX Ingress Controller&lt;/a&gt; além do &lt;strong&gt;NGINX&lt;/strong&gt; no nome.&lt;/p&gt;

&lt;p&gt;O primeiro, que é o assunto em ênfase deste post, é um subprojeto mantido pelo próprio Kubernetes. O segundo é mantido pelos próprios mantenedores do NGINX, cuja empresa foi comprada pela F5.&lt;/p&gt;

&lt;p&gt;São softwares diferentes, mantidos por pessoas diferentes, com parametrizações e funcionalidades diferentes e - o mais importante - incompatíveis entre si. Não é tão raro assim ver usuários instalando a segunda solução pensando ser a primeira, e se perguntando porque as configurações aplicadas não estão funcionando!&lt;/p&gt;

&lt;p&gt;E aqui vem um dos aspectos mais importantes do entendimento de Ingress no Kubernetes: por ser um objeto exageradamente simples, e balanceamento de carga em camada 7 ser algo &lt;strong&gt;extremamente complexo&lt;/strong&gt;, existe um volume &lt;strong&gt;absurdo&lt;/strong&gt; de configurações que dependem de implementação, e são ativados de maneiras diferentes. &lt;strong&gt;Ingress Controllers&lt;/strong&gt;, via de regra, &lt;strong&gt;não são&lt;/strong&gt; facilmente intercambiáveis, especialmente se você fizer uso de funcionalidades avançadas. E certas configurações, mesmo que disponíveis em outros Controllers (como &lt;a href="https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#client-certificate-authentication" rel="noopener noreferrer"&gt;o encaminhamento de certificados TLS para a aplicação&lt;/a&gt;), podem exigir &lt;strong&gt;reescrita do código da sua aplicação&lt;/strong&gt; porque são implementadas de maneira diferentes por cada um.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quais os problemas do Ingress NGINX Controller?
&lt;/h2&gt;

&lt;p&gt;Componentes de software de infraestrutura geralmente precisam executar com privilégios especiais em clusters Kubernetes.&lt;/p&gt;

&lt;p&gt;E aqui está um dos principais problemas do Ingress NGINX Controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ingress-nginx
rules:
- apiGroups:
  - &lt;span class="s2"&gt;""&lt;/span&gt;
  resources:
  - configmaps
  - endpoints
  - nodes
  - pods
  - secrets
  - namespaces
  verbs:
  - list
  - watch
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A instalação padrão, que imagino ser usada por 99,99% dos usuários, dá permissão para acessar &lt;strong&gt;todos os Secrets de um cluster&lt;/strong&gt;. Isso geralmente é necessário porque uma das funcionalidades do objeto &lt;strong&gt;Ingress&lt;/strong&gt; costuma ser ler certificados TLS a partir de &lt;strong&gt;Secrets&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devsres-tls&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;subs.devsres.com&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subs-devsres-com&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subs.devsres.com&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devsres&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um atacante capaz de comprometer o Ingress Controller daria acesso não só a todos os certificados (e chaves privadas destes), como outras credenciais importantes normalmente salvas em &lt;strong&gt;Secrets&lt;/strong&gt; (de bancos de dados, serviços, chaves de API). &lt;/p&gt;

&lt;p&gt;Além disso, se você criar um &lt;strong&gt;token de ServiceAccount persistente&lt;/strong&gt;, aos moldes do que era comum no Kubernetes até a versão 1.22, o atacante também poderia acessá-los e potencialmente escalar privilégios.&lt;/p&gt;

&lt;p&gt;Como todo componente de infraestrutura, entender sobre a segurança do Ingress Controller é essencial, em especial pelo fato de ser este geralmente o único componente exposto na Internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Validating Webhook Configuration"
&lt;/h2&gt;

&lt;p&gt;Existe um objeto nativo do Kubernetes chamado &lt;strong&gt;validatingwebhookconfiguration&lt;/strong&gt; que entrega para os usuários uma funcionalidade interessante: a de &lt;strong&gt;inspecionar as operações em recursos na API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;Ingress NGINX Controller&lt;/strong&gt; faz uso desse recurso para monitorar a criação e atualização dos objetos &lt;strong&gt;Ingress&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admissionregistration.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ValidatingWebhookConfiguration&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-nginx-admission&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;webhooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;admissionReviewVersions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
  &lt;span class="na"&gt;clientConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-nginx-controller-admission&lt;/span&gt;
      &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-nginx&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/networking/v1/ingresses&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
  &lt;span class="na"&gt;failurePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fail&lt;/span&gt;
  &lt;span class="na"&gt;matchPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Equivalent&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate.nginx.ingress.kubernetes.io&lt;/span&gt;
  &lt;span class="na"&gt;namespaceSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
  &lt;span class="na"&gt;objectSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io&lt;/span&gt;
    &lt;span class="na"&gt;apiVersions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
    &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CREATE&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;UPDATE&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ingresses&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
  &lt;span class="na"&gt;sideEffects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;None&lt;/span&gt;
  &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para que ele faz isso? Para evitar &lt;strong&gt;conflito entre configurações&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Como o objeto &lt;strong&gt;Ingress&lt;/strong&gt; define um conjunto bastante simplório de configurações e sua implementação fica ao encargo de outro software, é possível que as configurações definidas sejam incompatíveis entre si.&lt;/p&gt;

&lt;p&gt;Inclusive, uma infelicidade do &lt;strong&gt;Ingress&lt;/strong&gt; é que uma configuração em uma &lt;strong&gt;Namespace&lt;/strong&gt; pode impactar negativamente aplicações em &lt;strong&gt;outra Namespace&lt;/strong&gt; - é o único caso em todo o Kubernetes em que consigo imaginar algo desse tipo de ser possível!&lt;/p&gt;

&lt;p&gt;Por exemplo, caso definamos o seguinte objeto Ingress para receber as conexões feitas ao balanceador sem escopo definido:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aplicacao1&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este segundo objeto define uma configuração que conflita diretamente com a primeira: elas não podem existir no mesmo cluster!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;desastre&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aplicacao2&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;desastre&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado final de conflitos de configurações deste tipo é imprevisível: pode ser que a segunda substitua a primeira, ou que o &lt;strong&gt;Controller&lt;/strong&gt; identifique o conflito e escolha o mais antigo, ou o pior dos casos, que costuma ser a geração de uma configuração inválida que faz com que o Nginx não consiga mais atualizar suas configurações, deixando o Ingress "estagnado" e inconsistente com o ambiente, incapaz de executar as reconciliações necessárias para um ambiente super dinâmico. &lt;/p&gt;

&lt;p&gt;Para evitar isso, o &lt;strong&gt;Ingress NGINX Controller&lt;/strong&gt; implementa essa validação  &lt;strong&gt;antes&lt;/strong&gt; da criação ou modificação de objetos, fazendo com que o Kubernetes encaminhe o objeto para um programa (que, no caso, é o próprio ingress-nginx-controller):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; ingress2.yaml
Error from server &lt;span class="o"&gt;(&lt;/span&gt;AlreadyExists&lt;span class="o"&gt;)&lt;/span&gt;: error when creating &lt;span class="s2"&gt;"ingress2.yaml"&lt;/span&gt;: ingresses.networking.k8s.io &lt;span class="s2"&gt;"ingress1"&lt;/span&gt; already exists
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problemas do Webhook
&lt;/h2&gt;

&lt;p&gt;O principal problema é o fato do &lt;strong&gt;Controller&lt;/strong&gt; estar acessível pela rede:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dentro do container, você pode ver o processo que escuta na porta 8443:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;netstat &lt;span class="nt"&gt;-nptlwev&lt;/span&gt; | fgrep 8443
tcp6       0      0 :::8443                 :::&lt;span class="k"&gt;*&lt;/span&gt;                    LISTEN      101        174698     7/nginx-ingress-controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um atacante que consiga comprometer um &lt;strong&gt;Pod&lt;/strong&gt; qualquer em execução (ou criar um, ainda que sem qualquer privilégio especial) terá acesso livre ao Webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl run shellhacker  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shellhacker
pod/shellhacker created

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; shellhacker &lt;span class="nt"&gt;--&lt;/span&gt; /bin/bash
root@shellhacker:/# curl &lt;span class="nt"&gt;-kv&lt;/span&gt; https://ingress-nginx-controller-admission.ingress-nginx:443
&lt;span class="k"&gt;*&lt;/span&gt;   Trying 10.100.66.184:443...
&lt;span class="k"&gt;*&lt;/span&gt; Connected to ingress-nginx-controller-admission.ingress-nginx &lt;span class="o"&gt;(&lt;/span&gt;10.100.66.184&lt;span class="o"&gt;)&lt;/span&gt; port 443 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="c"&gt;#0)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Host: ingress-nginx-controller-admission.ingress-nginx
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; User-Agent: curl/7.88.1
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; TLSv1.3 &lt;span class="o"&gt;(&lt;/span&gt;IN&lt;span class="o"&gt;)&lt;/span&gt;, TLS handshake, Newsession Ticket &lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;)&lt;/span&gt;:
&amp;lt; HTTP/1.1 400 Bad Request
&amp;lt; Date: Mon, 31 Mar 2025 07:35:47 GMT
&amp;lt; Content-Length: 0
&amp;lt;
&lt;span class="k"&gt;*&lt;/span&gt; Connection &lt;span class="c"&gt;#0 to host ingress-nginx-controller-admission.ingress-nginx left intact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Felizmente, a maior parte das instalações padrão não expõe o acesso ao &lt;strong&gt;Controller&lt;/strong&gt; diretamente na Internet, apenas internamente ao cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; ingress-nginx get services
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;                      AGE
ingress-nginx-controller             LoadBalancer   10.100.246.156   a26b9332b9a154edc9b6d741dc759fdd-1876731234.us-west-2.elb.amazonaws.com   80:32691/TCP,443:31242/TCP   85m
ingress-nginx-controller-admission   ClusterIP      10.100.66.184    &amp;lt;none&amp;gt;                                                                    443/TCP                      85m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas pode acontecer que usuários mais desavisados acabem por expô-lo também! Mais uma coisa para prestar atenção nas suas instalações!&lt;/p&gt;

&lt;h2&gt;
  
  
  Vulnerabilidade CVE 2025-1974 e demais
&lt;/h2&gt;

&lt;p&gt;O uso do &lt;strong&gt;Ingress NGINX Controller&lt;/strong&gt; na forma de webhook para este recurso de validação é justamente o assunto da vulnerabilidade mais grave - a &lt;a href="https://kubernetes.io/blog/2025/03/24/ingress-nginx-cve-2025-1974/" rel="noopener noreferrer"&gt;CVE 2025-1974&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;As outras vulnerabilidades dependem da criação de objetos &lt;strong&gt;Ingresses&lt;/strong&gt; no cluster formatadas de maneira maliciosa; essa operação é considerada privilegiada e a maior parte dos workloads em execução em um cluster não tem permissão para fazê-lo, tornando-as perigosas mas mais difíceis de explorar. &lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;CVE 2025-1974&lt;/strong&gt; é muito mais grave que as outras porque você &lt;strong&gt;não precisa da permissão para criar Ingresses&lt;/strong&gt;; basta ter acesso de rede ao &lt;strong&gt;Controller&lt;/strong&gt; com acesso ao Webhook para enviar um objeto &lt;strong&gt;Ingress&lt;/strong&gt; devidamente formatado que pode, entre outras coisas, gerar execução de código malicioso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como remediar?
&lt;/h2&gt;

&lt;p&gt;As correções já saíram e os ambientes já podem ser atualizados.&lt;/p&gt;

&lt;p&gt;Caso a atualização não seja possível, o importante é ao menos bloquear os acessos ao Controller pela rede de &lt;strong&gt;Pods&lt;/strong&gt;. A maneira mais comum de fazer isso é simplesmente removendo o &lt;strong&gt;ValidatingWebhookconfiguration ingress-nginx-admission&lt;/strong&gt;, mas também pode ser feito por meio do uso de &lt;strong&gt;NetworkPolicies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O uso de &lt;strong&gt;NetworkPolicies&lt;/strong&gt;, costumeiramente ignorado, é uma excelente solução para mitigar este tipo de risco. Nenhum software, por mais maduro que seja, está livre de falhas desta natureza. O importante, neste caso, é sempre se antecipar e impedir ser pego de surpresa por algo desta gravidade.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>ingress</category>
      <category>nginx</category>
    </item>
    <item>
      <title>From which Kubernetes pod (and namespace!) is this process that I see on my host?</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Fri, 16 Aug 2024 05:28:01 +0000</pubDate>
      <link>https://dev.to/aws-builders/from-which-kubernetes-pod-and-namespace-is-this-process-that-i-see-on-my-host-5c0n</link>
      <guid>https://dev.to/aws-builders/from-which-kubernetes-pod-and-namespace-is-this-process-that-i-see-on-my-host-5c0n</guid>
      <description>&lt;p&gt;So you figured out that the pids of your Kubernetes (or Docker) containers show up on your host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ps auxw | fgrep python
root       11282  0.0  0.0  94628 24372 ?        Ss   04:53   0:00 python app.py
root       11439  0.0  0.0  41968 13072 ?        Ss   04:53   0:00 python event-simulator.py
root       12527  0.0  0.0   6796  2292 pts/0    S+   04:57   0:00 &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auto python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what Kubernetes pod owns these processes?&lt;/p&gt;

&lt;p&gt;As far as my knowledge extends, there is not a trivial way to use a native command to figure it out. You have to put the pieces of the puzzles by yourself.&lt;/p&gt;

&lt;p&gt;The usual "hack" I see most people do is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nsenter &lt;span class="nt"&gt;-t&lt;/span&gt; 11282 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nb"&gt;hostname
&lt;/span&gt;webapp-color

&lt;span class="nv"&gt;$ &lt;/span&gt;nsenter &lt;span class="nt"&gt;-t&lt;/span&gt; 11439 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nb"&gt;hostname
&lt;/span&gt;e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that does not give me the Kubernetes namespace. &lt;/p&gt;

&lt;p&gt;I did some random googling and couldn't figure out someone else that is as annoyed as myself for this, so I decided to write this blog post.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL:DR edition&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;11282 11439&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;crictl inspect &lt;span class="nt"&gt;--output&lt;/span&gt; go-template &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{index .status.labels "io.kubernetes.pod.namespace" }}:{{index .status.labels "io.kubernetes.pod.name" }}'&lt;/span&gt;  &lt;span class="si"&gt;$(&lt;/span&gt; &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; /proc/&lt;span class="nv"&gt;$i&lt;/span&gt;/cgroup | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f5&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done
&lt;/span&gt;default:webapp-color
e-commerce:e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to learn how and why it works, keep reading.&lt;/p&gt;




&lt;p&gt;You probably read somewhere that what makes containers real are two Linux resources: &lt;strong&gt;cgroups&lt;/strong&gt; and &lt;strong&gt;namespaces&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Let's go back to the "hack":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nsenter &lt;span class="nt"&gt;-t&lt;/span&gt; 11439 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nb"&gt;hostname
&lt;/span&gt;e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Linux command &lt;strong&gt;nsenter&lt;/strong&gt; allows you to run a command &lt;strong&gt;inside the namespace&lt;/strong&gt; of another process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nsenter &lt;span class="nt"&gt;-h&lt;/span&gt;
Run a program with namespaces of other processes.
...
 &lt;span class="nt"&gt;-u&lt;/span&gt;, &lt;span class="nt"&gt;--uts&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]     enter UTS namespace &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hostname &lt;/span&gt;etc&lt;span class="o"&gt;)&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the hack uses &lt;strong&gt;nsenter -u&lt;/strong&gt;, which in turns run a command inside the Linux &lt;a href="https://www.baeldung.com/linux/uts-namespaces" rel="noopener noreferrer"&gt;&lt;strong&gt;UTS Namespace&lt;/strong&gt;&lt;/a&gt;, which allows us to set &lt;strong&gt;hostnames&lt;/strong&gt; and &lt;strong&gt;domain names&lt;/strong&gt; without affecting the rest of the system. &lt;/p&gt;

&lt;p&gt;The UTS namespace is definitely not exactly the most recognizable; when people think of namespaces, usually they remember the &lt;a href="https://www.baeldung.com/linux/list-namespaces#2-process-id-namespace" rel="noopener noreferrer"&gt;&lt;strong&gt;PID namespace&lt;/strong&gt;&lt;/a&gt;, responsible to provide an isolated process ID space for the container, or the &lt;a href="https://www.baeldung.com/linux/list-namespaces#3-network-namespace" rel="noopener noreferrer"&gt;&lt;strong&gt;Network namespace&lt;/strong&gt;&lt;/a&gt;, which in turn provides an isolated network stack. But yeah, there are a bunch of other ones, as you can check out on the links in this paragraph. Or in case you're lazy, here is an extended output of the &lt;strong&gt;nsenter -h&lt;/strong&gt; output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nsenter &lt;span class="nt"&gt;-h&lt;/span&gt;
Usage:
 nsenter &lt;span class="o"&gt;[&lt;/span&gt;options] &lt;span class="o"&gt;[&lt;/span&gt;&amp;lt;program&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;&amp;lt;argument&amp;gt;...]]

Run a program with namespaces of other processes.
...
 &lt;span class="nt"&gt;-a&lt;/span&gt;, &lt;span class="nt"&gt;--all&lt;/span&gt;              enter all namespaces
 &lt;span class="nt"&gt;-t&lt;/span&gt;, &lt;span class="nt"&gt;--target&lt;/span&gt; &amp;lt;pid&amp;gt;     target process to get namespaces from
 &lt;span class="nt"&gt;-m&lt;/span&gt;, &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]   enter mount namespace
 &lt;span class="nt"&gt;-u&lt;/span&gt;, &lt;span class="nt"&gt;--uts&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]     enter UTS namespace &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;hostname &lt;/span&gt;etc&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="nt"&gt;-i&lt;/span&gt;, &lt;span class="nt"&gt;--ipc&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]     enter System V IPC namespace
 &lt;span class="nt"&gt;-n&lt;/span&gt;, &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]     enter network namespace
 &lt;span class="nt"&gt;-p&lt;/span&gt;, &lt;span class="nt"&gt;--pid&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]     enter pid namespace
 &lt;span class="nt"&gt;-C&lt;/span&gt;, &lt;span class="nt"&gt;--cgroup&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]  enter cgroup namespace
 &lt;span class="nt"&gt;-U&lt;/span&gt;, &lt;span class="nt"&gt;--user&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]    enter user namespace
 &lt;span class="nt"&gt;-T&lt;/span&gt;, &lt;span class="nt"&gt;--time&lt;/span&gt;&lt;span class="o"&gt;[=&lt;/span&gt;&amp;lt;file&amp;gt;]    enter &lt;span class="nb"&gt;time &lt;/span&gt;namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was a fun one! But what about &lt;strong&gt;cgroups&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Well, &lt;a href="https://www.baeldung.com/linux/limit-resource-consumption" rel="noopener noreferrer"&gt;cgroups&lt;/a&gt; allow us to limit the use of system resources like CPU, memory and network bandwith.&lt;/p&gt;

&lt;p&gt;You can see the cgroups of a process with an easy &lt;em&gt;ps&lt;/em&gt;*:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ps &lt;span class="nt"&gt;-wwo&lt;/span&gt; &lt;span class="nv"&gt;cgroup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; 11439
12:hugetlb:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,11:cpu,cpuacct:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,10:memory:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,9:perf_event:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,8:net_cls,net_prio:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,7:cpuset:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,6:pids:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,5:blkio:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,4:devices:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,3:rdma:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,2:freezer:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a,1:name&lt;span class="o"&gt;=&lt;/span&gt;systemd:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ouch, that was ugly. Let's prettify the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps &lt;span class="nt"&gt;-wwo&lt;/span&gt; &lt;span class="nv"&gt;cgroup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; 11439 | &lt;span class="nb"&gt;tr&lt;/span&gt; , &lt;span class="s1"&gt;'\n'&lt;/span&gt;
12:hugetlb:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
11:cpu
cpuacct:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
10:memory:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
9:perf_event:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
8:net_cls
net_prio:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
7:cpuset:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
6:pids:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
5:blkio:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
4:devices:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
3:rdma:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
2:freezer:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
1:name&lt;span class="o"&gt;=&lt;/span&gt;systemd:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better. &lt;/p&gt;

&lt;p&gt;But truth be told, it is basically the same output of this simple cat command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/11439/cgroup 
12:hugetlb:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
11:cpu,cpuacct:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
10:memory:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
9:perf_event:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
8:net_cls,net_prio:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
7:cpuset:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
6:pids:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
5:blkio:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
4:devices:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
3:rdma:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
2:freezer:/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
1:name&lt;span class="o"&gt;=&lt;/span&gt;systemd:/system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
0::/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What each of them do? Well, you can read this &lt;a href="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#controllers" rel="noopener noreferrer"&gt;from the Linux Kernel itself&lt;/a&gt;, and that's not the focus of today's post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We need to figure out which Kubernetes pod (and it namespace!) is the owner of that proces&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;If you pay attention, you'll notice an interesting pattern on the cgroups naming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/11439/cgroup | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f3-&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt;  &lt;span class="nt"&gt;-c&lt;/span&gt; 
      1 /
      6 /kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
      6 /system.slice/containerd.service/kubepods-besteffort-pod86811ae3_b633_4eb8_a508_e3eae190f6ce.slice:cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets discard the part of the hierarchy that we do not care that much about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/11439/cgroup | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f4-&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt;  &lt;span class="nt"&gt;-c&lt;/span&gt; 
      1 
     12 cri-containerd:da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That character sequence after the ':' seems awfully familiar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl ps &lt;span class="nt"&gt;--id&lt;/span&gt; da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
da1bdd84c8b25       ee4be8f9dfd10       7 minutes ago       Running             simple-webapp       0                   48ee11c897fa8       e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here it is! The pod name! &lt;/p&gt;

&lt;p&gt;But what about its namespace?&lt;/p&gt;

&lt;p&gt;I'm not sure if there is an easy way to make crictl return this to you. I know the annoying one: formatting its output using go-template!&lt;/p&gt;

&lt;p&gt;Let's see what we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a | jq &lt;span class="s1"&gt;'. | keys'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"info"&lt;/span&gt;,
  &lt;span class="s2"&gt;"status"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go deeper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a | jq &lt;span class="s1"&gt;'.info | keys'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"config"&lt;/span&gt;,
  &lt;span class="s2"&gt;"pid"&lt;/span&gt;,
  &lt;span class="s2"&gt;"removing"&lt;/span&gt;,
  &lt;span class="s2"&gt;"runtimeOptions"&lt;/span&gt;,
  &lt;span class="s2"&gt;"runtimeSpec"&lt;/span&gt;,
  &lt;span class="s2"&gt;"runtimeType"&lt;/span&gt;,
  &lt;span class="s2"&gt;"sandboxID"&lt;/span&gt;,
  &lt;span class="s2"&gt;"snapshotKey"&lt;/span&gt;,
  &lt;span class="s2"&gt;"snapshotter"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a | jq &lt;span class="s1"&gt;'.status | keys'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"annotations"&lt;/span&gt;,
  &lt;span class="s2"&gt;"createdAt"&lt;/span&gt;,
  &lt;span class="s2"&gt;"exitCode"&lt;/span&gt;,
  &lt;span class="s2"&gt;"finishedAt"&lt;/span&gt;,
  &lt;span class="s2"&gt;"id"&lt;/span&gt;,
  &lt;span class="s2"&gt;"image"&lt;/span&gt;,
  &lt;span class="s2"&gt;"imageId"&lt;/span&gt;,
  &lt;span class="s2"&gt;"imageRef"&lt;/span&gt;,
  &lt;span class="s2"&gt;"labels"&lt;/span&gt;,
  &lt;span class="s2"&gt;"logPath"&lt;/span&gt;,
  &lt;span class="s2"&gt;"message"&lt;/span&gt;,
  &lt;span class="s2"&gt;"metadata"&lt;/span&gt;,
  &lt;span class="s2"&gt;"mounts"&lt;/span&gt;,
  &lt;span class="s2"&gt;"reason"&lt;/span&gt;,
  &lt;span class="s2"&gt;"resources"&lt;/span&gt;,
  &lt;span class="s2"&gt;"startedAt"&lt;/span&gt;,
  &lt;span class="s2"&gt;"state"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have a hunch we want to see what is stored on .status.labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a | jq &lt;span class="s1"&gt;'.status.labels'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"io.kubernetes.container.name"&lt;/span&gt;: &lt;span class="s2"&gt;"simple-webapp"&lt;/span&gt;,
  &lt;span class="s2"&gt;"io.kubernetes.pod.name"&lt;/span&gt;: &lt;span class="s2"&gt;"e-com-1123"&lt;/span&gt;,
  &lt;span class="s2"&gt;"io.kubernetes.pod.namespace"&lt;/span&gt;: &lt;span class="s2"&gt;"e-commerce"&lt;/span&gt;,
  &lt;span class="s2"&gt;"io.kubernetes.pod.uid"&lt;/span&gt;: &lt;span class="s2"&gt;"86811ae3-b633-4eb8-a508-e3eae190f6ce"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh yeah! That is what we want! We have a &lt;strong&gt;pod&lt;/strong&gt; called &lt;strong&gt;e-com-1123&lt;/strong&gt; running the process. It is in the Kubernetes &lt;strong&gt;namespace&lt;/strong&gt; &lt;strong&gt;e-commerce&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Let's create a go-template from it. This is how you access the members of a map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect &lt;span class="nt"&gt;--output&lt;/span&gt; go-template &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{.status.labels}}'&lt;/span&gt; da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
map[io.kubernetes.container.name:simple-webapp io.kubernetes.pod.name:e-com-1123 io.kubernetes.pod.namespace:e-commerce io.kubernetes.pod.uid:86811ae3-b633-4eb8-a508-e3eae190f6ce]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can imagine, if a member of a map has a '.' on its name, you'll incurr into problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect &lt;span class="nt"&gt;--output&lt;/span&gt; go-template &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{.status.labels.io.kubernetes.pod.name}}'&lt;/span&gt; da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
FATA[0000] getting the status of the container &lt;span class="s2"&gt;"da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a"&lt;/span&gt;: failed to template data: template: tmplExecuteRawJSON:1:9: executing &lt;span class="s2"&gt;"tmplExecuteRawJSON"&lt;/span&gt; at &amp;lt;.status.labels.io.kubernetes.pod.name&amp;gt;: map has no entry &lt;span class="k"&gt;for &lt;/span&gt;key &lt;span class="s2"&gt;"io"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No worries, just use one of the very few builtin go-template functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect &lt;span class="nt"&gt;--output&lt;/span&gt; go-template &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{index .status.labels "io.kubernetes.pod.name" }}'&lt;/span&gt; da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are halfway there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crictl inspect &lt;span class="nt"&gt;--output&lt;/span&gt; go-template &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{index .status.labels "io.kubernetes.pod.namespace" }}:{{index .status.labels "io.kubernetes.pod.name" }}'&lt;/span&gt; da1bdd84c8b25938081afe48da7075e2a211d2b1a62e01c894b4e5f3ffab670a
e-commerce:e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, turning into a one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt; pgrep python &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;crictl inspect &lt;span class="nt"&gt;--output&lt;/span&gt; go-template &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s1"&gt;'{{index .status.labels "io.kubernetes.pod.namespace" }}:{{index .status.labels "io.kubernetes.pod.name" }}'&lt;/span&gt;  &lt;span class="si"&gt;$(&lt;/span&gt; &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; /proc/&lt;span class="nv"&gt;$i&lt;/span&gt;/cgroup | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f5&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done
&lt;/span&gt;default:webapp-color
e-commerce:e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the answer I've been looking: how to figure out from which namespace and pod a process on my host is running.&lt;/p&gt;




&lt;p&gt;I admit making it a oneliner didn't help all that much with legibility, so I will write a script in hopes to make it easier to understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;which-pod-am-i.sh 
&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# This is NOT a production script!&lt;/span&gt;
&lt;span class="c"&gt;# This was made just to make the techniques legible!&lt;/span&gt;

&lt;span class="c"&gt;# Pieces of go templates.&lt;/span&gt;
&lt;span class="c"&gt;# We need to retrieve two fields from .status.labels:&lt;/span&gt;
&lt;span class="c"&gt;# .status.labels."io.kubernetes.pod.namespace" &lt;/span&gt;
&lt;span class="nv"&gt;NAMESPACE_TMPL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{index .status.labels "io.kubernetes.pod.namespace" }}'&lt;/span&gt;
&lt;span class="c"&gt;# .status.labels."io.kubernetes.pod.name"&lt;/span&gt;
&lt;span class="nv"&gt;POD_TMPL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{index .status.labels "io.kubernetes.pod.name" }}'&lt;/span&gt;

&lt;span class="c"&gt;# I'm passing the process name from command line: &lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt; pgrep &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;?Parameter not passed&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 

  &lt;span class="c"&gt;# Get the container ID from the cgroups:&lt;/span&gt;
  &lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt; &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; /proc/&lt;span class="nv"&gt;$i&lt;/span&gt;/cgroup | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f5&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;# Inspect with go templates:   &lt;/span&gt;
  crictl inspect &lt;span class="nt"&gt;--output&lt;/span&gt; go-template &lt;span class="se"&gt;\&lt;/span&gt;
                 &lt;span class="nt"&gt;--template&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NAMESPACE_TMPL&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$POD_TMPL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                 &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;:?Container&lt;span class="p"&gt; not found&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;./which-pod-am-i.sh python
default:webapp-color
e-commerce:e-com-1123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, all these commands were run on &lt;a href="https://kodekloud.com" rel="noopener noreferrer"&gt;KodeKloud Sandbox&lt;/a&gt; provided with their &lt;a href="https://www.udemy.com/course/certified-kubernetes-application-developer/" rel="noopener noreferrer"&gt;Udemy CKAD course&lt;/a&gt;. Shout out to them for providing an incredibly &lt;br&gt;
affordable and non-expirable hands-on resource to study for CNCF CKA/CKAD certifications. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Terraform, OpenTofu e criptografia de estado</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Tue, 02 Jul 2024 03:49:25 +0000</pubDate>
      <link>https://dev.to/aws-builders/terraform-opentofu-and-state-encryption-4am1</link>
      <guid>https://dev.to/aws-builders/terraform-opentofu-and-state-encryption-4am1</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: o &lt;strong&gt;OpenTofu&lt;/strong&gt; agora conta com criptografia de &lt;strong&gt;State files&lt;/strong&gt; (e de &lt;strong&gt;Plan files&lt;/strong&gt; também!) sem depender do backend remoto. O &lt;strong&gt;Terraform&lt;/strong&gt; não - e talvez nunca suporte em sua versão gratuita.&lt;/p&gt;




&lt;p&gt;É de extrema importância conhecer bem as ferramentas que você pretende usar para ser capaz de desenhar bons fluxos de trabalho e, especialmente, mapear os riscos envolvidos.&lt;/p&gt;

&lt;p&gt;Quem usa Terraform já está familiarizado com a relevância do &lt;strong&gt;State File&lt;/strong&gt; e que é de extrema importância garantir não só a resiliência do arquivo como também o controle de acesso.&lt;/p&gt;

&lt;p&gt;No post abaixo, vamos fazer um estudo de caso demonstrando os problemas de ter um arquivo de State File em texto claro disponível em buckets S3.&lt;/p&gt;




&lt;p&gt;Vamos supor que você criou uma variável no &lt;strong&gt;AWS Secrets Manager&lt;/strong&gt; com &lt;strong&gt;Terraform&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws sts get-caller-identity
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"UserId"&lt;/span&gt;: &lt;span class="s2"&gt;"AIDA2UC3CSEZOOZQXHZCN"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Account"&lt;/span&gt;: &lt;span class="s2"&gt;"730335449394"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Arn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::730335449394:user/cloud_user"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;aws secretsmanager get-secret-value &lt;span class="nt"&gt;--secret-id&lt;/span&gt; senha_root
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ARN"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:secretsmanager:us-east-1:730335449394:secret:senha_root-qiNJDs"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Name"&lt;/span&gt;: &lt;span class="s2"&gt;"senha_root"&lt;/span&gt;,
    &lt;span class="s2"&gt;"VersionId"&lt;/span&gt;: &lt;span class="s2"&gt;"terraform-20240701215533811100000001"&lt;/span&gt;,
    &lt;span class="s2"&gt;"SecretString"&lt;/span&gt;: &lt;span class="s2"&gt;"z0mgp4ssw0rd"&lt;/span&gt;,
    &lt;span class="s2"&gt;"VersionStages"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"AWSCURRENT"&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"CreatedDate"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-07-01T18:55:32.978000-03:00"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um usuário &lt;strong&gt;hacker&lt;/strong&gt; &lt;strong&gt;não vai ter acesso à variável&lt;/strong&gt;, a menos que alguém dê acesso explícito para ele:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws sts get-caller-identity
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"UserId"&lt;/span&gt;: &lt;span class="s2"&gt;"AIDA2UC3CSEZLYNWRDSTL"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Account"&lt;/span&gt;: &lt;span class="s2"&gt;"730335449394"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Arn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:iam::730335449394:user/hacker"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;aws secretsmanager get-secret-value &lt;span class="nt"&gt;--secret-id&lt;/span&gt; senha_root

An error occurred &lt;span class="o"&gt;(&lt;/span&gt;AccessDeniedException&lt;span class="o"&gt;)&lt;/span&gt; when calling the GetSecretValue operation: User: arn:aws:iam::730335449394:user/hacker is not authorized to perform: secretsmanager:GetSecretValue on resource: senha_root because no identity-based policy allows the secretsmanager:GetSecretValue action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe que o erro é claramente permissão:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;no identity-based policy allows the secretsmanager:GetSecretValue action&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Caso o hacker consiga comprometer um usuário que &lt;strong&gt;conte com acesso ao Secrets Manager&lt;/strong&gt;, ainda é possível usar criptografia de uma chave KMS específica (e não a padrão do Secrets Manager), o que adiciona uma segunda camada de segurança:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agora o hacker conseguiu comprometer um usuário com acesso ao SM!&lt;/span&gt;
&lt;span class="c"&gt;# Isso deu a ele a chance de acessar secrets menos importantes:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;aws secretsmanager get-secret-value &lt;span class="nt"&gt;--secret-id&lt;/span&gt; senha_menos_importante
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ARN"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:secretsmanager:us-east-1:730335449394:secret:senha_menos_importante-Y4reR7"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Name"&lt;/span&gt;: &lt;span class="s2"&gt;"senha_menos_importante"&lt;/span&gt;,
    &lt;span class="s2"&gt;"VersionId"&lt;/span&gt;: &lt;span class="s2"&gt;"e919b63c-6937-44ca-81b9-b8b2172c6b33"&lt;/span&gt;,
    &lt;span class="s2"&gt;"SecretString"&lt;/span&gt;: &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;SENHA&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;menos_importante&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;,
    &lt;span class="s2"&gt;"VersionStages"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"AWSCURRENT"&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"CreatedDate"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-07-01T20:22:02.007000-03:00"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# A senha_root, entretanto, está criptografada com uma chave KMS não padrão:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;aws secretsmanager get-secret-value &lt;span class="nt"&gt;--secret-id&lt;/span&gt; senha_root

An error occurred &lt;span class="o"&gt;(&lt;/span&gt;AccessDeniedException&lt;span class="o"&gt;)&lt;/span&gt; when calling the GetSecretValue operation: Access to KMS is not allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, a mensagem de erro mudou:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Access to KMS is not allowed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para acessar a senha importante, é necessário não só acesso ao serviço Secrets Manager, mas também ao serviço de gestão de chaves &lt;strong&gt;KMS&lt;/strong&gt;, que normalmente conta com &lt;strong&gt;Resource Policies&lt;/strong&gt; mais restritas de acordo com a finalidade.&lt;/p&gt;

&lt;p&gt;Só que... Geralmente guardamos arquivos de &lt;strong&gt;Terraform State&lt;/strong&gt; em S3! &lt;/p&gt;

&lt;p&gt;E se o hacker comprometer um usuário com acesso a buckets S3?&lt;/p&gt;

&lt;h2&gt;
  
  
  Acessando arquivos de Terraform State
&lt;/h2&gt;

&lt;p&gt;Um arquivo &lt;strong&gt;Terraform State&lt;/strong&gt; é um JSON escrito em &lt;strong&gt;texto claro&lt;/strong&gt;, sem qualquer proteção aos valores salvos. &lt;/p&gt;

&lt;p&gt;Podemos até escrever o código Terraform da seguinte maneira:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;variables.tf
variable &lt;span class="s2"&gt;"senha_root"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; string
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Senha de root mais importante que temos"&lt;/span&gt;
  sensitive &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true
  &lt;/span&gt;nullable &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note o atributo &lt;strong&gt;sensitive&lt;/strong&gt;! &lt;/p&gt;

&lt;p&gt;Podemos também garantir que o parâmetro só é conhecido em tempo de execução de código por meio de passagem de parâmetro do usuário:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Um formulário permitiu preencher a variável SENHA antes da execução:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;terraform apply &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s2"&gt;"senha_root=&lt;/span&gt;&lt;span class="nv"&gt;$SENHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Só que, na prática, o atributo &lt;strong&gt;sensitive&lt;/strong&gt; apenas garante que o atributo não será exibido na saída da execução do comando ou em mensagens de log, como descrito &lt;a href="https://developer.hashicorp.com/terraform/tutorials/configuration-language/sensitive-variables"&gt;aqui&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Terraform will then redact these values in the output of Terraform commands or log messages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No mesmo link acima, um alerta importante em outra seção:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you run Terraform commands with a local state file, Terraform stores the state as plain text, including variable values, even if you have flagged them as sensitive. Terraform needs to store these values in your state so that it can tell if you have changed them since the last time you applied your configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Traduzindo: o Terraform salva o State File em &lt;strong&gt;texto claro&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;E mais abaixo:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Marking variables as sensitive is not sufficient to secure them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Como eles mesmos descrevem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You must also keep them secure while passing them into Terraform configuration...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;...como fizemos com o formulário, mas...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...and protect them in your state file. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claro que você pode usar as versões pagas para resolver seu problema:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HCP Terraform and Terraform Enterprise manage and share sensitive values, and encrypt all variable values before storing them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Caso contrário, você está sem sorte, como na sequência de comandos abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3api list-buckets &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Buckets[*].Name'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
devsres-terraform-state-storage

&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--recursive&lt;/span&gt; s3://devsres-terraform-state-storage/
2024-07-01 20:30:15       3987 terraform/state/senha_root

&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://devsres-terraform-state-storage/terraform/state/senha_root /tmp/
download: s3://devsres-terraform-state-storage/terraform/state/senha_root to /tmp/senha_root

&lt;span class="c"&gt;# A maioria das pessoas simplesmente usaria um grep mesmo:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.resources[].instances[].attributes | select(.secret_string != null).secret_string'&lt;/span&gt; /tmp/senha_root
z0mgM1nh4p@ssw0rd!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Opentofu terraform state encryption
&lt;/h2&gt;

&lt;p&gt;Só que isso é se estivermos falando do &lt;strong&gt;Terraform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;OpenTofu&lt;/strong&gt; é um projeto software livre derivado do Terraform após a mudança de licença da Hashicorp para BSL.&lt;/p&gt;

&lt;p&gt;Em vez de ser um simples "fork", o OpenTofu trouxe novas funcionalidades que respondem ao anseio de muitas pessoas da comunidade, na prática deixando os códigos agora nem sempre 100% compatíveis!&lt;/p&gt;

&lt;p&gt;Por exemplo, é possível &lt;strong&gt;criptografar o Terraform State&lt;/strong&gt; com uma passphrase ou com um serviço de chaves como o KMS &lt;strong&gt;sem precisar contar com funcionalidades nativas do S3&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Como fazer isso?&lt;/p&gt;

&lt;p&gt;Bem, aí é necessário &lt;a href="https://opentofu.org/docs/language/state/encryption/"&gt;ler a documentação&lt;/a&gt;. Essa funcionalidade está disponível desde a versão &lt;a href="https://github.com/opentofu/opentofu/releases/tag/v1.7.0"&gt;1.7.0&lt;/a&gt; lançada em abril de 2024.&lt;/p&gt;

&lt;p&gt;O exemplo abaixo é suficiente para mostrar a criptografia com passphrase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;terraform.tf
terraform &lt;span class="o"&gt;{&lt;/span&gt;
  backend &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    bucket &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"devsres-terraform-state-storage"&lt;/span&gt;
    key    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tofu/state/senha_root"&lt;/span&gt;
    region &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

    &lt;span class="c"&gt;# encrypt    = true&lt;/span&gt;
    &lt;span class="c"&gt;# kms_key_id = "arn:aws:kms:us-east-1:730335449394:key/9b8acab0-df09-4c2b-81ab-7dd33d2a4ba2"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  encryption &lt;span class="o"&gt;{&lt;/span&gt;
    key_provider &lt;span class="s2"&gt;"pbkdf2"&lt;/span&gt; &lt;span class="s2"&gt;"senha_de_state"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      passphrase &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"wow!criptografia!"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    method &lt;span class="s2"&gt;"aes_gcm"&lt;/span&gt; &lt;span class="s2"&gt;"protege"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      keys &lt;span class="o"&gt;=&lt;/span&gt; key_provider.pbkdf2.senha_de_state
    &lt;span class="o"&gt;}&lt;/span&gt;

    state &lt;span class="o"&gt;{&lt;/span&gt;
      method &lt;span class="o"&gt;=&lt;/span&gt; method.aes_gcm.protege
      enforced &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao usar o código acima, este é o &lt;strong&gt;State File&lt;/strong&gt; disponível no bucket S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://devsres-terraform-state-storage/tofu/state/senha_root /tmp/
t download: s3://devsres-terraform-state-storage/tofu/state/senha_root to ../../../../../tmp/senha_root

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/senha_root
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"serial"&lt;/span&gt;:1,&lt;span class="s2"&gt;"lineage"&lt;/span&gt;:&lt;span class="s2"&gt;"5d8b4611-05a3-1f8a-404a-11e1b0693e8f"&lt;/span&gt;,&lt;span class="s2"&gt;"meta"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"key_provider.pbkdf2.senha_de_state"&lt;/span&gt;:&lt;span class="s2"&gt;"eyJzYWx0IjoiN3d4VmgxcHF3S01EQ0FzRXpqZ0xnU0w3bkJGbmFSd2pmSGVwWm45VTlkOD0iLCJpdGVyYXRpb25zIjo2MDAwMDAsImhhc2hfZnVuY3Rpb24iOiJzaGE1MTIiLCJrZXlfbGVuZ3RoIjozMn0="&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"encrypted_data"&lt;/span&gt;:&lt;span class="s2"&gt;"YihB4BhoJnJ/RV0tmk+CzHDrs6YBQOqVPf+JllkbiMQex0U9TglBOb+iLo1OQRIw4EyXCPDgqwdTM7yx+OFp2bCiiP+mQC5dk6pv6SQiRLKc1rvVV3pfrpvnts59s8SYSFZOzOTJfZgOeEaGKueGdgmkbQR2hXE6BRh0o3ohi/Zxw67B5pKtYNdNJFQPgLbBYDhNP/a5pCpsNi2PYw6I4xILbqLKgQPMmspQPS4+zIP2IU71VnW9anirJp5trV9dMb0v2qyzZLY4iitsbgltRYziCJURh/e4vR7P1oTbaI0OZ422r1uJLIWyrKzZH12pqtqjNmT/KXIGXtd76TcqjmMxX+s1jtyvV5F6EnwCU857DfGyBl/0WgREPo7kB9uH6MhwOCkUPTe638awLp+8Ess9cKFzxx/jn8u4GvtkBEJoTUO/0fFVh0fIudtYaRuiNuJ4Hd/rMlgfXc/smo50su23jeHEJfycksWnwjXIreMLzkXevQChZ/ix3RyPxH55dZIivyyBaILIvaP+1dEzzgCNiuD0chtxOTqOEkJnh30exznLRLR/8q3ISmN86co2C1fvs7PwmpTyYlDb33DzwmpYMbdJ79dngM4nc7d2drv6kDxyi2+KYdGKZmO1r56lQgxPewCNQPwzyC7OOp/rNWB4K2s9favdKQ2Fh3m7JtOi9PWzMTvHPZVEylam3RxbpH4IRoDEpGXw9tzRCBFNXbmBwEDTNKYM2yuDMyrqIQj0PuTitsRWtfScPZtZRs7ar0UmeAvG5Pn/H9Z3WEwk/um9Yogob7HAYsMnsBZyRoCrGxg1nJYFt65WPzcxmqbx/tO3mHwOL75RLQ6bicmVLIHbXTI92zc1OZ9uf80QIMVA4Qs3FlKXGW4rOufEFXUd1d31qoM4nZaxPB3kEJmNUiuKIaKbvcHCnbkSKsQP+WfmBYFCbBXSbwf9R5EXdU6kigX3Ixg5pkyXkeZNUXoFA707Xq6QuwWWihuAfbq16zPIpmWzXZPRiGOpAFGV5tJE4/ZXkL0l/cDf7mbkzN7/mK18wFXCWHeBBhi/KDNHpyvrFyO/RM5cLAnYQR0GBZ1FYPmVASYTwoF/m+T/zfZQMLXsinrFhmBaPOuhT5/v3O6X6S0DaMJYoQiupOPXXVwJ2Go/TGWw00AZ8J4I4mn+yz7rAG5VZWiz4H08Z3VuAWO9O5J6EYiMqGsU10DEftNy1juao3UmlnjZx8XkRNmgkDCfNHXx3+eo8mu5vGds9HrvsGYpUVX0rQQtzh5JIcUOQz9Yh/BNsRSxZ+7Y5SY45ifq1rf6kjaA0R8gBEoPvAAOE2QBZaKkq+wrtiJ4TiXJk7fni2nsSv6c29pONaQMq7E9TVdL5LhS8vzl69ticbheAJpoDUOR2R2Bf0FnpFx9d2P1mckCBqhIIuQQqMPjL+0/az4ysAA6k/LF+xfH7WBxl6RfUFHzfXty792z0HkueF62Y+1a9VRJViuaSl5f5QJ90tYnHsM3ooAb6c1m+tTevxE2nyIIvb2bzOyh7tOLVFSgJzzuX/C5VXGQ6KdeRCVQFcGP+XuodZQEhkkpWBxVIHXJfSAEFNQPvKGJJdkadz5zbyw1GJbY6CSwYB/rc5v1H6FDc1YSLNRTk2d6cEI71rtURuuYrO9K/Rq9EowPwdGRO+eom8Wu/QMsyGiab6hiAbek8frq4zAxhcOHjXTbJG0KkvteBKwNJfDeLKQ5A/JyjAIdlAK9zEBtuf+kBWNoQN85izVwftMQghpCVFIlUEqYA0Lbgo0a4bYvcDuCn6lIaekY2o2qoeS+CBKR0vp4DYFxVOBhAET92Sk1fODeZflvGkISJPMyXcKC1j1f50wN/X+cTxkb/+7r/IILbMtcxoD3sFIs29SgacaYizM/VKuAIcj7XMD9z7qTE7LQvbU+UL9C1ZyCms8havyrGkyODDWO8TNDI2bASzLtc08a9c3lAFVafqYn0ioSuP+FKpb5HopNeyEXh1U30sp1gmkPT/KF4VQdNt6joQ1El5SdpZra68IOkLrG92wKiWUPenAND+ubZYh5AX5fIOSUCAFZIzj1akGE9HJ1aA4+pHiIbC51dVvjtOVJnYWSkvOyiQteZs3Hmrr003R7COSVVbJqCYnUfcDr6HnyNT+ucoPMMpx1M1A7A3w4UQcfvxHEqOOITI/o7WMW52ZpMzBjc+o8fwSq2ybFA5SPetXTQnmyqD7B9Cx0/2s7J6neMsDPaM8H1gmHXZQBE4VPPAvoe6CrSy87I4Np3b2XH6+0coHBPChaI6Y0NGj+qqgOtKB/JSDERAU5zUELbhevPpAqOw71Cq/kuGMMC8PBAiO8Nflkkmx8Lv5qIwrAAu7NpFE4zjYAVtZw04SQxFXIxhqnXwqYkQoI/6ABMKKIacctGov1n1vIJYTC84pLHVqEnX+gGbA7JaCpewIdHvgOecgaA7l2/bSKNwck5J+7koD1GxOO9aM2YQvFpYa3VjbVTGSGzAglXaM40r+NVj/qjPbGdB/CoAnSIRfVPnJgoKtQHFC4fltm3IWJ2Xf/kQm3h4b/sks4V8AIwr6IGSE6ZtwsPA4tDDumbta2iGuZrEw9VQDad0kYJkMVZ767Kh4dx1os55jk7lbOvv9j0g7ryFMjmZx1NzfqswFa4e23yvyHpYoKjLxV2pFq2rByZifdPEWGrp37Zt7NgJtHU3wdjQSVuiB5UeX7q/1Kr8PDRWtRNxaZ55KmZhIswqwr7uji+qnsWGxq7hRtpcnPZk4GWN1GenpBHX+T/YTsylXBAOvpHWnj+r3YgcwkhTn4lrPZ3Ta9CXxMZMG2Cj1c3yvqfAHDDQ6uVHKXnai+vWJFUQvyPPsTU5njn3kBcptwnSptNPqzZnrE26qy6E8qK9bS6a1YhYjbw9J6W8ORJacJYfQj5OabGTcaBLC6zzY9GF9LsUy9WoVb8Fph5ydJLXkyARJwSlvWpzKc4psLKhtjcYIzBoEHg9SBTDrk4LKtiRjVaaa5sjjixib1O7fUkZEREG6IzhsJ3AOarRbSb1J1KpF5iC5Pc31yjkkt6yBstBqYLqINRff5nrNzT61i28ZwTZRIQtV9hb4JotJo50JJsHaN7idalZ47teRrIlvXQpOF3dkO1BHRN4rfEUHuIBV9/cdUz9ylqyQtb+qPmK7LD246H9bRZRj8e80DJtfwPBFjCPXYMCHO2a22aGIMjFXTeo1IHIdwvFIeevSHzzyDhfViknaGu6B7fEM5fGKmAbEZ5IpGxGK22UfXQDXHgOns0IDBUovgS5dlN0Ag2/swCS5i4ys/nzi01AJfm1HMfu7nIisBK39FJLDJlDycQjcvdk3tuwYktI+4uybnXl03PaE6re7+FV6FFveXYfpCrrHasAOs/Cj9isgbk+/vSSDVeS5324/DawlV54JCUsUA512ad167gCGZJcFOrxTl9vXke4fw2iHnX00q6sx3drNZ4LJxynSxiI0wF3i7Fm5c9poVQC2Tgko3usNDM8eojjVbi2e7dAPtWRi3rkAxT7Ad/RNY9EGcYg0j5KqOjeWm/leQhsgeqCQ1DVUeRCYb6q1w0Ghahd1ecmvEtm3HgtEOtBD6tPrgD5deUEjrnKLEUPKAiZMg3g5AnMEWEYVBfo/gpc37wp+luMjs3phHe2sx/l1eHOUrOR57jLxqsxOOh5FivBRv/RT2amc5n8PzA6RMLYPweF1kA5005TimawYHEfMf0bWoVe+arCeyk1MCrAKg+WlbzFC4UKH/wCYcD+Cy+kWAdfjuQxhCX3jmkgyTDO0dRzHEqbMv4NJn9zTmC3PqdA6JoXAgRvQhqYdY8x2iQA6SqUBu0K1D4BYPROSoJAp0T0e4zRAKzN2C6CS51DNYEFPS8XsvWemKYdkTKuohBfukUdyq8qz0LosLC1SmkMKxRo3iQGHJcBL+u/n1eUeHQJzwO1S79KibdwXyXOkV+m0/2TEYlzUFF5fy+kUBDPgnUPkiHNb23qTGXnry7vxE0L4Wv3hwwuHu9Au1a7ot3V/1pHcdeHusL+peR9npTVriMsAVzt4tCQDweVTgMwSuXi1J/bNo13hHR9tvw8jpJ7SW5avqTHxJT6g2PaOeztfT6h7CcxOJ1LHFUZKwLq4NNjMxJebdcX0sdYeOdO/ZsHfAN4RJbashe67ulqsoxk/rjRFkp/pbeXoC43TUByKxGcHUNpPs3u6gMH4L2mSsFs4+Euvk0gBhKqiO+CBtSauggZKrXzM9UFJsjtBNL6OSaPip3NVIG2bwUd1bhjLrdicVAGZBnoa6pIIBHndNMQCepZsPpM45OGQFDtpUIDvlrzO2uqlUqS87HYWz5hKfUri9nUwhBRnrkFkjbjchQMGDX9QcWHlfiS4GYT41OZfXWjbtrdOCw0CAvCaMH1906M4dZ3n3KnidtC0E9HU/Gq3hPNZPqN4iE4xuL9zresPyvjxWpdWzYOkmKP5qtqN5qN1xCW3Yv7HVHCblT9NbpIOKP4VbHQ4yXgrx5VQ1lDSueMFA71mvDGT7Dzdx299TH1v54oZmIofXC5suFSG7qNtlN+nbRlp6aGHeljaI6SF4vVjCBrH+H32lglJk2z27eT6jYXpnw3mfRw82czhOgBKOTFMNxFz17ydd3BQ5laBSn/6CNnLdwdhBmD8Agoyw4xDbu4YWmJtAy3OiiRRNF69KGg1+RqfaWaADpH8RLA4Bjlo00GwBUj4jeecFWjrGMbQaYwewUww/IVBt6BJR/qJ5gJpRQKnAe+NVWkrVrZk0PtEyNbMVLmWQrMPrK8nyUiZfrCmMzmpeUrNq5428Niegw+qbNRc35HbLxSOUuuDvkNytrkbuj7EAvwLlzgrAv43Mb5+0p5ywu+0+FrdbRPuivA7DysmFKFWNIF7yyLp8eqmHk0BtCzfMqWOL3ubUfht0T3o5P2eKuikRktslY5Vmwe9X3yUWMOU97UUHrfCFEU4MtNBe1lr2ylLccYE7PaBMwRnHgBum24WKAqlOfFV8FlU5wj2+J+VL1tR2OXdnjD03q9FfAi2MyYzIzY7HWF9ckw9bEb+kqQ1U0zXdtf0JYa0uZCO+ec14zIlvQm8GDspaMpf/y1cHgb4aI5dvD2OYE0CYucxfxVD9/Et68UbrIaXJNrD3LanALTLYKWOEtQOI+Gs1nFGp8SwWArRHJTsJN1LV8TcfdY6aGNBsMx74"&lt;/span&gt;,&lt;span class="s2"&gt;"encryption_version"&lt;/span&gt;:&lt;span class="s2"&gt;"v0"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O arquivo está criptografado e indisponível para acesso direto.&lt;/p&gt;

&lt;p&gt;Ah, e se você tentar usar esse bloco de código com o Terraform, ele trará um erro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform init &lt;span class="nt"&gt;-migrate-state&lt;/span&gt;
...

╷
│ Error: Unsupported block &lt;span class="nb"&gt;type&lt;/span&gt;
│
│   on main.tf line 10, &lt;span class="k"&gt;in &lt;/span&gt;terraform:
│   10:   encryption &lt;span class="o"&gt;{&lt;/span&gt;
│
│ Blocks of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s2"&gt;"encryption"&lt;/span&gt; are not expected here.
╵
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extra: Mitigação na AWS com KMS
&lt;/h2&gt;

&lt;p&gt;Uma pessoa mais sagaz poderia argumentar: "ah, é só usar a chave KMS para criptografar &lt;strong&gt;também&lt;/strong&gt; o seu State File"!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat 
&lt;/span&gt;terraform &lt;span class="o"&gt;{&lt;/span&gt;
  backend &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    bucket &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"devsres-terraform-state-storage"&lt;/span&gt;
    key    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform/state/senha_root"&lt;/span&gt;
    region &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

    encrypt    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true
    &lt;/span&gt;kms_key_id &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:kms:us-east-1:730335449394:key/9b8acab0-df09-4c2b-81ab-7dd33d2a4ba2"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E é verdade, isso mitiga o acesso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://devsres-terraform-state-storage/terraform/state/senha_root /tmp/
download failed: s3://devsres-terraform-state-storage/terraform/state/senha_root to ../../tmp/senha_root An error occurred &lt;span class="o"&gt;(&lt;/span&gt;AccessDenied&lt;span class="o"&gt;)&lt;/span&gt; when calling the GetObject operation: User: arn:aws:iam::730335449394:user/hacker is not authorized to perform: kms:Decrypt on resource: arn:aws:kms:us-east-1:730335449394:key/9b8acab0-df09-4c2b-81ab-7dd33d2a4ba2 because no identity-based policy allows the kms:Decrypt action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas nem todo backend suporta esta funcionalidade. Salvo engano, não é possível usar com &lt;a href="https://github.com/hashicorp/terraform/issues/30944"&gt;Azure Blobs&lt;/a&gt;; outros backends e usuários on-premises também ficam sem opção.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>opentofu</category>
      <category>encryption</category>
    </item>
    <item>
      <title>Como a AWS Cloud Control API e o novo Terraform provider impactam sua vida?</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Thu, 06 Jun 2024 03:56:10 +0000</pubDate>
      <link>https://dev.to/aws-builders/como-a-aws-cloud-control-api-e-o-novo-terraform-provider-impactam-sua-vida-3jg8</link>
      <guid>https://dev.to/aws-builders/como-a-aws-cloud-control-api-e-o-novo-terraform-provider-impactam-sua-vida-3jg8</guid>
      <description>&lt;p&gt;Recentemente, a Hashicorp anunciou que o provider AWS Cloud Control agora é considerável pronto para uso em produção (também conhecido como "Generally Available", ou &lt;strong&gt;GA&lt;/strong&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasi8tf3s8puoycd04lpa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasi8tf3s8puoycd04lpa.png" alt="Hashicorp Headline: Terraform AWS Cloud Control API provider now generally available" width="789" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A notícia é bem explicativa, mas percebi bastante confusão dentre meus conhecidos a respeito sobre o assunto, então vamos falar sobre! O que é AWS Cloud Control, como funciona, o que faz este novo provider e qual o impacto na sua vida!&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform Provider AWS
&lt;/h2&gt;

&lt;p&gt;Se você usa Terraform (ou OpenTofu) e AWS, então provavelmente você usa o &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest"&gt;Terraform Provider AWS&lt;/a&gt; para provisionar sua infraestrutura.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6b1u74xm1j0lh24a3hx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6b1u74xm1j0lh24a3hx.png" alt="Screenshot of the Terraform Provider AWS page, current version 5.52.0" width="739" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disponível desde o &lt;strong&gt;Terraform 0.1&lt;/strong&gt; (2014!), este possivelmente é o provider mais antigo da  solução e de longe o mais conhecido e usado.&lt;/p&gt;

&lt;p&gt;O que a notícia compartilhada tem a ver com este provider? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nada&lt;/strong&gt;! &lt;/p&gt;

&lt;p&gt;Este provider usa a mesma API que a &lt;strong&gt;AWS SDK&lt;/strong&gt; e o &lt;strong&gt;AWS CLI&lt;/strong&gt;, por exemplo.&lt;/p&gt;

&lt;p&gt;Por falar em AWS CLI...&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS API e suas inconsistências
&lt;/h2&gt;

&lt;p&gt;Se você já usou a &lt;strong&gt;AWS CLI&lt;/strong&gt;, provavelmente já percebeu algumas "particularidades". &lt;/p&gt;

&lt;p&gt;As APIs da AWS foram construídas ao longo de quase 20 anos; serviços de épocas diferentes certamente foram desenvolvidos por equipes diferentes a partir de modelos e ideias diferentes. Além disso, serviços antigos são extendidos possivelmente por outras equipes que implementam recursos do seu jeito. &lt;/p&gt;

&lt;p&gt;Combinado ao tamanho e variedade de ofertas, essa característica torna torna a API &lt;strong&gt;extremamente inconsistente&lt;/strong&gt; em seu comportamento. Vamos a um exemplo bem simplório mas que ilustra adequadamente o que queremos dizer.&lt;/p&gt;

&lt;p&gt;Para listar as máquinas virtuais de uma determinada região, você executa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws ec2 describe-instances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para listar seus bancos gerenciados do serviço RDS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws rds describe-db-instances
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por outro lado, se você quiser  os buckets S3 de uma conta, você usa o seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para listar todas as Kinesis streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws kinesis list-streams
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O subcomando &lt;strong&gt;s3&lt;/strong&gt; da AWS CLI é tão esquisito que a AWS optou por implementar uma nova API para trabalhar com s3 e não quebrar a retrocompatibilidade - o subcomando &lt;strong&gt;s3api&lt;/strong&gt;. Este subcomando usa a versão mais "moderna", parecida com o Kinesis, que usa &lt;strong&gt;list&lt;/strong&gt; como verbo em vez de &lt;strong&gt;describe&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3api list-buckets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se você achou bobagem essas divergências, saiba que isso tem seu custo, em especial se você vai além das operações triviais. Como essas particularidades não são da linha de comando e sim da API, elas se apresentam também na &lt;strong&gt;AWS SDK&lt;/strong&gt;, e isso pode ser um problema, em especial para quem desenvolve soluções &lt;strong&gt;third-party&lt;/strong&gt; que integram com serviços AWS. Observe o &lt;a href="https://www.lastweekinaws.com/blog/how-aws-dumps-the-mental-burden-of-inconsistent-apis-on-developers/"&gt;post abaixo&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkbhote4wmqnvvlx8ua7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkbhote4wmqnvvlx8ua7.png" alt="Screenshot com uma notícia sobre a inconsistência da API" width="507" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Então eu não sou o primeiro a apontar que isso incomoda!&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Cloud Control
&lt;/h2&gt;

&lt;p&gt;Curiosamente, no mesmo ano da notícia anterior, a &lt;a href="https://aws.amazon.com/blogs/aws/announcing-aws-cloud-control-api/"&gt;AWS anunciou a &lt;strong&gt;API Cloud Control&lt;/strong&gt;&lt;/a&gt;, cujo propósito é resolver justamente esse problema de consistência:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jxzqte6p6jrq1t1l138.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jxzqte6p6jrq1t1l138.png" alt="Anúncio da AWS sobre Cloud Control API" width="512" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A própria AWS admite os problemas:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As applications and infrastructures become increasingly sophisticated and you work across more AWS services, it becomes increasingly difficult to learn and manage distinct APIs. This challenge is exacerbated when you also use third-party services in your infrastructure, since you have to build and maintain custom code to manage both the AWS and third-party services together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Qual era a ideia então? Oferecer uma "interface padrão" para as operações em todos os serviços da AWS. No caso, implementar as operações de um CRUDL (o mesmo que um CRUD, mas com um &lt;strong&gt;List&lt;/strong&gt; junto!):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CreateResource;&lt;/li&gt;
&lt;li&gt;GetResource;&lt;/li&gt;
&lt;li&gt;UpdateResource;&lt;/li&gt;
&lt;li&gt;DeleteResource;&lt;/li&gt;
&lt;li&gt;ListResource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como funciona na prática? Vamos criar um &lt;strong&gt;bucket S3&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol create-resource &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--type-name&lt;/span&gt; AWS::S3::Bucket &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--desired-state&lt;/span&gt; &lt;span class="s1"&gt;'{
        "BucketName": devsres-cloudcontrol-lab
        }'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ProgressEvent"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::S3::Bucket"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"devsres-cloudcontrol-lab"&lt;/span&gt;,
        &lt;span class="s2"&gt;"RequestToken"&lt;/span&gt;: &lt;span class="s2"&gt;"6b47f322-d9af-4d08-8faa-6d530f33bcff"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Operation"&lt;/span&gt;: &lt;span class="s2"&gt;"CREATE"&lt;/span&gt;,
        &lt;span class="s2"&gt;"OperationStatus"&lt;/span&gt;: &lt;span class="s2"&gt;"IN_PROGRESS"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EventTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-06-05T22:16:06.220000-03:00"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criando uma &lt;strong&gt;stream Kinesis&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol create-resource &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--type-name&lt;/span&gt; AWS::Kinesis::Stream &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--desired-state&lt;/span&gt; &lt;span class="s1"&gt;'{
      "Name": "ResourceExample",
      "RetentionPeriodHours":168, 
      "ShardCount\":3
      }'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quer criar um CloudWatch LogGroup?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol create-resource &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--type-name&lt;/span&gt; AWS::Logs::LogGroup   &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--desired-state&lt;/span&gt; &lt;span class="s1"&gt;'{
   "LogGroupName": "DevSREsCloudControlLG",
   "RetentionInDays":30
   }'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ProgressEvent"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::Logs::LogGroup"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"DevSREsCloudControlLG"&lt;/span&gt;,
        &lt;span class="s2"&gt;"RequestToken"&lt;/span&gt;: &lt;span class="s2"&gt;"7fe86619-dda1-4698-beb0-7bcf29c22868"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Operation"&lt;/span&gt;: &lt;span class="s2"&gt;"CREATE"&lt;/span&gt;,
        &lt;span class="s2"&gt;"OperationStatus"&lt;/span&gt;: &lt;span class="s2"&gt;"IN_PROGRESS"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EventTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-06-04T00:56:17.364000-03:00"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para listar os buckets existentes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol list-resources &lt;span class="nt"&gt;--type-name&lt;/span&gt; AWS::S3::Bucket
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ResourceDescriptions"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"devsres-cloudcontrol-lab"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Properties"&lt;/span&gt;: &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;BucketName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;devsres-cloudcontrol-lab&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"outro-bucket-criado-de-outro-jeito"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Properties"&lt;/span&gt;: &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;BucketName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;outro-bucket-criado-de-outro-jeito&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"terceiro-bucket-criado-de-outro-jeito"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Properties"&lt;/span&gt;: &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;BucketName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;terceiro-bucket-criado-de-outro-jeito&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::S3::Bucket"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quer listar os CloudWatch LogGroups?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol list-resources &lt;span class="nt"&gt;--type-name&lt;/span&gt; AWS::Logs::LogGroup
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ResourceDescriptions"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"DevSREsCloudControlLG"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Properties"&lt;/span&gt;: &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;RetentionInDays&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:30,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;LogGroupClass&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;STANDARD&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;LogGroupName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;DevSREsCloudControlLG&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Arn&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:logs:us-west-2:211125357951:log-group:DevSREsCloudControlLG:*&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"OutroLogGroup"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Properties"&lt;/span&gt;: &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;RetentionInDays&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:30,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;LogGroupClass&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;STANDARD&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;LogGroupName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;OutroLogGroup&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Arn&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:logs:us-west-2:211125357951:log-group:OutroLogGroup:*&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"TerceiroLogGroup"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Properties"&lt;/span&gt;: &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;RetentionInDays&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:30,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;LogGroupClass&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;STANDARD&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;LogGroupName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;TerceiroLogGroup&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Arn&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:logs:us-west-2:211125357951:log-group:TerceiroLogGroup:*&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::Logs::LogGroup"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Não sei se você prestou atenção, mas a solicitação de criação não é concluída de maneira síncrona:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol create-resource &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--type-name&lt;/span&gt; AWS::S3::Bucket       &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--desired-state&lt;/span&gt; &lt;span class="s1"&gt;'{
        "BucketName": "ele-nao-espera-concluir"
      }'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ProgressEvent"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::S3::Bucket"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"ele-nao-espera-concluir"&lt;/span&gt;,
        &lt;span class="s2"&gt;"RequestToken"&lt;/span&gt;: &lt;span class="s2"&gt;"e871792e-1467-45cb-bd0d-9ac8041825e2"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Operation"&lt;/span&gt;: &lt;span class="s2"&gt;"CREATE"&lt;/span&gt;,
        &lt;span class="s2"&gt;"OperationStatus"&lt;/span&gt;: &lt;span class="s2"&gt;"IN_PROGRESS"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EventTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-06-05T23:52:02.801000-03:00"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;em&gt;OperationStatus&lt;/em&gt; &lt;strong&gt;IN_PROGRESS&lt;/strong&gt; indica que a solicitação está sendo processada. Para acompanhar o status, usa-se outro comando passando o &lt;em&gt;RequestToken&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol get-resource-request-status &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--request-token&lt;/span&gt; e871792e-1467-45cb-bd0d-9ac8041825e2
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ProgressEvent"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::S3::Bucket"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"ele-nao-espera-concluir"&lt;/span&gt;,
        &lt;span class="s2"&gt;"RequestToken"&lt;/span&gt;: &lt;span class="s2"&gt;"e871792e-1467-45cb-bd0d-9ac8041825e2"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Operation"&lt;/span&gt;: &lt;span class="s2"&gt;"CREATE"&lt;/span&gt;,
        &lt;span class="s2"&gt;"OperationStatus"&lt;/span&gt;: &lt;span class="s2"&gt;"SUCCESS"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EventTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-06-05T23:52:25.022000-03:00"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A solicitação foi atendida com sucesso. &lt;/p&gt;

&lt;p&gt;E se tentarmos criar o mesmo bucket novamente?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol create-resource &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--type-name&lt;/span&gt; AWS::S3::Bucket       &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--desired-state&lt;/span&gt; &lt;span class="s1"&gt;'{
        "BucketName": "ele-nao-espera-concluir"
      }'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ProgressEvent"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::S3::Bucket"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"ele-nao-espera-concluir"&lt;/span&gt;,
        &lt;span class="s2"&gt;"RequestToken"&lt;/span&gt;: &lt;span class="s2"&gt;"c965b56a-7a25-4b70-b084-6e733db0c6c3"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Operation"&lt;/span&gt;: &lt;span class="s2"&gt;"CREATE"&lt;/span&gt;,
        &lt;span class="s2"&gt;"OperationStatus"&lt;/span&gt;: &lt;span class="s2"&gt;"IN_PROGRESS"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EventTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-06-05T23:57:55.038000-03:00"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;aws cloudcontrol get-resource-request-status &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--request-token&lt;/span&gt; c965b56a-7a25-4b70-b084-6e733db0c6c3
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ProgressEvent"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"TypeName"&lt;/span&gt;: &lt;span class="s2"&gt;"AWS::S3::Bucket"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Identifier"&lt;/span&gt;: &lt;span class="s2"&gt;"ele-nao-espera-concluir"&lt;/span&gt;,
        &lt;span class="s2"&gt;"RequestToken"&lt;/span&gt;: &lt;span class="s2"&gt;"c965b56a-7a25-4b70-b084-6e733db0c6c3"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Operation"&lt;/span&gt;: &lt;span class="s2"&gt;"CREATE"&lt;/span&gt;,
        &lt;span class="s2"&gt;"OperationStatus"&lt;/span&gt;: &lt;span class="s2"&gt;"FAILED"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EventTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-06-05T23:57:55.311000-03:00"&lt;/span&gt;,
        &lt;span class="s2"&gt;"StatusMessage"&lt;/span&gt;: &lt;span class="s2"&gt;"ele-nao-espera-concluir already exists (Service: S3, Status Code: 0, Request ID: null)"&lt;/span&gt;,
        &lt;span class="s2"&gt;"ErrorCode"&lt;/span&gt;: &lt;span class="s2"&gt;"AlreadyExists"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como era esperado, o retorno da requisição é uma falha, e &lt;em&gt;StatusMEssage&lt;/em&gt; te explica o porquê.&lt;/p&gt;

&lt;h2&gt;
  
  
  Genial! Quando migramos?
&lt;/h2&gt;

&lt;p&gt;Parece legal, certo? E realmente é.&lt;/p&gt;

&lt;p&gt;Mas você precisa se perguntar da relevância dessa mudança na sua vida e no seu código.&lt;/p&gt;

&lt;p&gt;Faz sentido você &lt;strong&gt;migrar&lt;/strong&gt; todas as suas &lt;em&gt;stacks&lt;/em&gt; de código Terraform para usar o novo &lt;em&gt;provider&lt;/em&gt;? &lt;/p&gt;

&lt;p&gt;A resposta, provavelmente, é &lt;strong&gt;não&lt;/strong&gt; - o ganho é mínimo para a infraestrutura pré-existente, em especial se levado em consideração a massiva quantidade de trabalho que essa migração demandaria.&lt;/p&gt;

&lt;p&gt;Faz sentido você passar a escrever todos os códigos Terraform daqui pra frente usando única e exclusivamente a API Cloud Control com seu novo &lt;em&gt;provider&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Aqui temos algo passível de discussão. Mas, &lt;strong&gt;na minha opinião&lt;/strong&gt;, não vejo como imperativa a troca. O provider acabou de ser lançado como &lt;strong&gt;GA&lt;/strong&gt;; a maturidade do software e a base de usuários que o adotou nem se comparam com o provider original. &lt;/p&gt;

&lt;p&gt;A própria API Cloud Control não tem tanta popularidade assim - em uma rápida pesquisa que fiz no meu perfil, quase ninguém havia sequer ouvido falar a respeito. Uma base menor de usuários no mínimo aumenta a possibilidade de incorrer em maior quantidades de bugs e comportamentos inesperados - tanto do provider quanto da própria API Cloud Control. &lt;/p&gt;

&lt;p&gt;Esse fato, por si só, não deveria desencorajar a adoção de um software, mas sabemos que nem todos estão prontos para se tornarem &lt;em&gt;Early Adopters&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Para quem é o Cloud Control então?
&lt;/h2&gt;

&lt;p&gt;A AWS responde sua pergunta no seu &lt;a href="https://aws.amazon.com/blogs/aws/announcing-aws-cloud-control-api/"&gt;anúncio de 2021&lt;/a&gt;: pessoas que construam soluções que operam usando serviços da AWS (especialmente se usarem &lt;strong&gt;muitos serviços&lt;/strong&gt;!). Trouxe o excerto em inglês original para cá:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Builders&lt;/strong&gt; - The first community is builders using AWS Services APIs to manage their infrastructure or their customer’s infrastructure. The ones requiring usage of low-level AWS Services APIs rather than higher level tools. For example, I know companies that manages AWS infrastructures on behalf of their clients. Many developed solutions to list and describe all resources deployed in their client’s AWS Accounts, for management and billing purposes. Often, they built specific tools to address their requirements, but find it hard to keep up with new AWS Services and features. Cloud Control API simplifies this type of tools by oﬀering a consistent, resource-centric approach. It makes easier to keep up with new AWS Services and features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;APN Partners&lt;/strong&gt; - The second community that benefits from Cloud Control API is APN Partners, such as HashiCorp (maker of Terraform) and Pulumi, and other APN Partners offering solutions that relies on AWS Services APIs. When AWS releases a new service or feature, our partner’s engineering teams need to learn, integrate, and test a new set of AWS Service APIs to expose it in their offerings. This is a time consuming process and often leads to a lag between the AWS release and the availability of the service or feature in their solution. With the new Cloud Control API, partners are now able to build a unique REST API code base, using unified API verbs, common input parameters, and common error types. They just have to merge the standardized pre-defined uniform resource model to interact with new AWS Services exposed as REST resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Se você não lê em inglês (melhore isso já!), vamos usar um exemplo.&lt;/p&gt;

&lt;p&gt;A AWS anunciou, no fim de abril, um novo serviço (nº 356?) de "render farms" gerenciado com o pouco sugestivo¹ nome &lt;strong&gt;Deadline Cloud&lt;/strong&gt;. Suponha que você trabalhe em uma empresa que faz trailers de filmes e tem interesse em usar este serviço; você &lt;strong&gt;não pode usar o terraform provider aws&lt;/strong&gt; simplesmente porque ele &lt;strong&gt;ainda não é suportado&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp210k1ye0a8k6k4upf1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp210k1ye0a8k6k4upf1v.png" alt="No matching records for Deadline on AWS Terraform Providers" width="446" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isso quer dizer que você não pode usar sua infra-estrutura de IaC com Terraform para usar este serviço, certo?&lt;/p&gt;

&lt;p&gt;Ou pode?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1m5dzn2xmzr2k6qmk8xn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1m5dzn2xmzr2k6qmk8xn.png" alt="18 matching results for Deadline on AWS Cloud Control Terraform Provider" width="430" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como a própria Hashicorp conta em seu &lt;a href="https://www.hashicorp.com/blog/terraform-aws-cloud-control-api-provider-now-generally-available"&gt;comunicado do post original&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Initially launched in 2021 as a tech preview, the Terraform AWS Cloud Control provider is automatically generated based on the Cloud Control API published by AWS, which means the latest features and services on AWS can be supported right away. The AWSCC provider gives developers access with several new AWS services such as: AWS Billing Conductor, AWS Chatbot, Amazon Personalize, Amazon Q Business, and more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Em uma tradução livre, o provider Cloud Control para Terraform é gerado automaticamente usando como base a API disponibilizada pela AWS. &lt;strong&gt;Teoricamente&lt;/strong&gt; isso quer dizer que qualquer funcionalidade disponibilizada pela AWS estará disponível quase que imediatamente.&lt;/p&gt;

&lt;p&gt;Mas há detalhes adicionais! A lista de serviços acima do excerto em inglês lista os serviços &lt;strong&gt;Amazon Q Business&lt;/strong&gt; (lançado em maio de 2024), &lt;strong&gt;AWS Billing Conductor&lt;/strong&gt; (lançado em 2022), &lt;strong&gt;AWS Chatbot&lt;/strong&gt; (lançado em 2020!) e &lt;strong&gt;Amazon Personalize&lt;/strong&gt;, que foi lançado há quase 5 anos atrás!&lt;/p&gt;

&lt;p&gt;Isso dá uma ideia que não é só uma questão de "usar os serviços lançados ontem", mas também usar serviços mais obscuros e menos usados, cuja baixa popularidade faz com que a implementação no provider principal não atinja massa crítica o suficiente para se justificar o tempo gasto (como o Amazon Personalize), oferecendo uma alternativa às pessoas que não conseguiam usar o mesmo padrão e convenções de IaC para 100% de sua infraestrutura na AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Para a Hashicorp e Pulumi, naturalmente seria muito melhor se todos abandonassem seus providers oiginais e migrassem para os novos baseados em Cloud Control. &lt;/p&gt;

&lt;p&gt;Mas esse desejo provavelmente não significa que irão descontinuar os providers originais e só oferecer manutenção nos novos - eles certamente não são loucos o suficiente para fazê-lo (ou são?).&lt;/p&gt;

&lt;p&gt;Se sua empresa está interessada em usar os serviços mais novos (em especial o festival de ofertas sobre generative AI), serviços mais obscuros (como o Amazon Personalize) ou simplesmente tem o perfil "early adopter", em que os funcionários contam com espaço para depurar e contribuir com projetos, lembre-se: a API Cloud Control existe, e agora temos inclusive um Terraform Provider considerado pronto para uso em produção.&lt;/p&gt;

&lt;p&gt;Aproveitem!&lt;/p&gt;




&lt;p&gt;¹ O nome do serviço não é tão pouco sugestivo assim; na verdade não é sugestivo para você! Em 2017, a AWS &lt;a href="https://techcrunch.com/2017/03/06/amazons-aws-buys-thinkbox-software-maker-of-tools-for-creative-professionals/"&gt;comprou uma empresa chamada Thinkbox Software&lt;/a&gt;, que construía, entre outras coisas, soluções para gerenciamento e pós processamento de renderizações. A mais popular das ferramentas se chama &lt;strong&gt;Deadline&lt;/strong&gt;; logo, &lt;strong&gt;AWS Deadline Cloud&lt;/strong&gt; é o deploy automatizado deste software na nuvem.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>EBS, seus snapshots e quanto ocupam de espaço</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Fri, 16 Feb 2024 01:22:50 +0000</pubDate>
      <link>https://dev.to/aws-builders/ebs-seus-snapshots-e-quanto-ocupam-de-espaco-4c39</link>
      <guid>https://dev.to/aws-builders/ebs-seus-snapshots-e-quanto-ocupam-de-espaco-4c39</guid>
      <description>&lt;p&gt;Se você for chato como eu, provavelmente não fica nada satisfeito com a seguinte informação fornecida pelos Snapshots dos seus discos EBS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws ec2 describe-snapshots &lt;span class="nt"&gt;--owner-ids&lt;/span&gt; self &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Snapshots[0]'&lt;/span&gt; | jq &lt;span class="s1"&gt;'{ "Id" :.SnapshotId, "Tamanho": .VolumeSize } '&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"Id"&lt;/span&gt;: &lt;span class="s2"&gt;"snap-0ad47bdaaf65a4a33"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Tamanho"&lt;/span&gt;: 300
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por que eu digo isso? Se você pegar uma lista com os snapshots diários realizados de um disco, todos eles vão ter esse tamanho "300", que é na verdade o tamanho do disco do qual foi tirado o snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt;  &lt;span class="s1"&gt;'.Snapshots[] | "\(.StartTime | sub("\\.[:+0-9]*"; "") | strptime("%Y-%m-%dT%H:%M:%S") | strftime("%Y-%m-%d %H:%M:%S") );\(.VolumeId);\(.SnapshotId);\(.VolumeSize)"'&lt;/span&gt;  /tmp/snapshots  | &lt;span class="nb"&gt;sort&lt;/span&gt; | fgrep 2024-01 | fgrep volumeAlvo | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;,3,4 &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt;
2024-01-12 06:52:14&lt;span class="p"&gt;;&lt;/span&gt;snap-1e61c51bff6bccb1c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-13 06:50:26&lt;span class="p"&gt;;&lt;/span&gt;snap-156ff8793f546c40c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-14 06:50:35&lt;span class="p"&gt;;&lt;/span&gt;snap-1540ae07ff3f2fafc&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-15 06:55:12&lt;span class="p"&gt;;&lt;/span&gt;snap-1deffec06fff3a76c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-16 06:50:55&lt;span class="p"&gt;;&lt;/span&gt;snap-1936dd665a700507c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-17 06:48:18&lt;span class="p"&gt;;&lt;/span&gt;snap-188a940760ef0c94c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-18 06:49:03&lt;span class="p"&gt;;&lt;/span&gt;snap-1945f27eae3a35e7c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-19 06:51:01&lt;span class="p"&gt;;&lt;/span&gt;snap-176d9fee762cfb56c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-20 06:50:14&lt;span class="p"&gt;;&lt;/span&gt;snap-115fe1148762c5f3c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-21 06:51:17&lt;span class="p"&gt;;&lt;/span&gt;snap-10d4a9af2fb8939fc&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-22 06:50:02&lt;span class="p"&gt;;&lt;/span&gt;snap-1e37bafc822cc7b4c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-23 06:50:51&lt;span class="p"&gt;;&lt;/span&gt;snap-11ee2feb0c80bf6cc&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-24 06:52:55&lt;span class="p"&gt;;&lt;/span&gt;snap-15c1f5bfd8c973b1c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-25 06:50:05&lt;span class="p"&gt;;&lt;/span&gt;snap-1236f4771ac4ff4fc&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-26 06:52:34&lt;span class="p"&gt;;&lt;/span&gt;snap-135de7fda2ca0039c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-27 06:54:12&lt;span class="p"&gt;;&lt;/span&gt;snap-1a2b6f44c3b250fac&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-28 06:49:42&lt;span class="p"&gt;;&lt;/span&gt;snap-15afe58c720ea661c&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-29 06:50:24&lt;span class="p"&gt;;&lt;/span&gt;snap-1c2cb9f83d180b5dc&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-30 06:51:31&lt;span class="p"&gt;;&lt;/span&gt;snap-19292f66969ae91cc&lt;span class="p"&gt;;&lt;/span&gt;300
2024-01-31 06:49:24&lt;span class="p"&gt;;&lt;/span&gt;snap-183c70ef3ce184b1c&lt;span class="p"&gt;;&lt;/span&gt;300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E isso não faz sentido, porque vamos relembrar a &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSSnapshots.html"&gt;definição de snapshot para a AWS&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can back up the data on your Amazon EBS volumes by making point-in-time copies, known as Amazon EBS snapshots. A snapshot is &lt;strong&gt;an incremental backup&lt;/strong&gt;, which means that we save only the blocks on the device that have changed since your most recent snapshot. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ou seja, o primeiro snapshot pode até ter próximo de 300 GiB; o segundo, entretanto, a menos que você reescreva o disco inteiro todos os dias, deverá ter &lt;strong&gt;bem menos blocos&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;E como fazer para saber o tamanho real de um snapshot? A resposta é que não há uma maneira conveniente.&lt;/p&gt;

&lt;p&gt;Então vamos de maneira inconveniente mesmo!&lt;/p&gt;

&lt;p&gt;A partir da saída acima, podemos gerar um arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt; jq &lt;span class="nt"&gt;-r&lt;/span&gt;  &lt;span class="s1"&gt;'.Snapshots[] | "\(.StartTime | sub("\\.[:+0-9]*"; "") | strptime("%Y-%m-%dT%H:%M:%S") | strftime("%Y-%m-%d %H:%M:%S") );\(.VolumeId);\(.SnapshotId);\(.VolumeSize)"'&lt;/span&gt;  /tmp/snapshots  | &lt;span class="nb"&gt;sort&lt;/span&gt; | fgrep 2024-01 | fgrep &amp;lt;volume&amp;gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;,3,4 &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/snaps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos comparar a diferença dos blocos do primeiro snapshot para o último:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws ebs list-changed-blocks  &lt;span class="nt"&gt;--first-snapshot-id&lt;/span&gt; snap-1e61c51bff6bccb1c &lt;span class="nt"&gt;--second-snapshot-id&lt;/span&gt; snap-183c70ef3ce184b1c  &lt;span class="nt"&gt;--max-results&lt;/span&gt; 100
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ChangedBlocks"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"BlockIndex"&lt;/span&gt;: 2,
            &lt;span class="s2"&gt;"FirstBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAWTl1T2vIApHeiKgKcuwBAiqZ3klr66mE47BZomkZvBk1xa7l6Se3EVy"&lt;/span&gt;,
            &lt;span class="s2"&gt;"SecondBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAZC7wx3B//8SZ8AO8Ik4nO3drO69ZHydPGfiFdQPf7+z0/yeTvhtpdPk"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"BlockIndex"&lt;/span&gt;: 3,
            &lt;span class="s2"&gt;"FirstBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAUFwRyPWnxXmE9tuUiGA0/IZNMT6QeArgY6LEUPzqfc1kla8X1RE682F"&lt;/span&gt;,
            &lt;span class="s2"&gt;"SecondBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAXZa8N6xIhaeQmNpB4Ryg0c71c3hfJggUam5aRWzXtsOMIM+o7B8gTMz"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"BlockIndex"&lt;/span&gt;: 9,
            &lt;span class="s2"&gt;"FirstBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAY/0eak3ujr8ZeaKojwpqpDdDgkEEoJbwm1qQq3ZD8OGgZJWWawJ3gAP"&lt;/span&gt;,
            &lt;span class="s2"&gt;"SecondBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAZMWcWKeWoOVSmSoddWfqIpt4MJb7jC8pczPc2Zb5bzDhaXdOcy5igEx"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"BlockIndex"&lt;/span&gt;: 54,
            &lt;span class="s2"&gt;"FirstBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAWVhyEKJPSo3dngCxFvu+thS+Wxb6bIsya3oW/7BhdXgKwXAEd1pf8le"&lt;/span&gt;,
            &lt;span class="s2"&gt;"SecondBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAZ/wRcFMm0dps8ORFkiSutXRI+UBS+Zx5sQ0nUonUTwBP6oQg0XcsTTI"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"BlockIndex"&lt;/span&gt;: 63,
            &lt;span class="s2"&gt;"FirstBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAbG6AUCqy8i8iTha8TsuS/KJjkR6m2orwTiWu6x1TRnhF9nZQeNryPNG"&lt;/span&gt;,
            &lt;span class="s2"&gt;"SecondBlockToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEBAa1CD96xB6tGbtqNlh2L0oRVcsQTWB5Zt2oFaYnF9r3Nl0BkdTXoMrgU"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="s2"&gt;"ExpiryTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-02-22T23:27:36.911000-03:00"&lt;/span&gt;,
    &lt;span class="s2"&gt;"VolumeSize"&lt;/span&gt;: 300,
    &lt;span class="s2"&gt;"BlockSize"&lt;/span&gt;: 524288,
    &lt;span class="s2"&gt;"NextToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEDAfg20tH5S6MgIQvwejESz9oJt6wTkV9E7uLE/AWOE3YldGqcD87vayC4iR409U/UlvchfJ31"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  "Temos 800 TiB de snapshots!"
&lt;/h2&gt;

&lt;p&gt;Em um outro caso, alguém ficou horrorizado porque tínhamos quase 800TiB de snapshots de um backup de um disco que não estava sendo usado!&lt;/p&gt;

&lt;p&gt;Mas... Se o disco não está sendo usado, os snapshots subsequentes possivelmente estão vazios, pois não há diferença entre os blocos!&lt;/p&gt;

&lt;p&gt;Vamos olhar?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt;  &lt;span class="s1"&gt;'.Snapshots[] | "\(.StartTime | sub("\\.[:+0-9]*"; "") | strptime("%Y-%m-%dT%H:%M:%S") | strftime("%Y-%m-%d %H:%M:%S") );\(.VolumeId);\(.SnapshotId);\(.VolumeSize)"'&lt;/span&gt;  /tmp/snapshots  | &lt;span class="nb"&gt;sort&lt;/span&gt; | fgrep 2024-01 | fgrep &amp;lt;outro volume&amp;gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;,3,4 &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/snaps

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; /tmp/snaps
2024-01-12 05:08:57&lt;span class="p"&gt;;&lt;/span&gt;snap-021dd9ce209e29e04&lt;span class="p"&gt;;&lt;/span&gt;8000
2024-01-13 05:04:54&lt;span class="p"&gt;;&lt;/span&gt;snap-0bb0b0a4ba1aa52a4&lt;span class="p"&gt;;&lt;/span&gt;8000
2024-01-14 05:05:34&lt;span class="p"&gt;;&lt;/span&gt;snap-0235d8d888956aabc&lt;span class="p"&gt;;&lt;/span&gt;8000
2024-01-15 05:05:45&lt;span class="p"&gt;;&lt;/span&gt;snap-0b438036484680c4d&lt;span class="p"&gt;;&lt;/span&gt;8000
2024-01-16 05:02:47&lt;span class="p"&gt;;&lt;/span&gt;snap-0224676f259ea425c&lt;span class="p"&gt;;&lt;/span&gt;8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos separar o primeiro snapshot (que tem o disco inteiro) e comparar com todos os demais:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;FIRST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt; &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; /tmp/snaps | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt; &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;+2 /tmp/snaps | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;: &lt;span class="p"&gt;;&lt;/span&gt; aws ebs list-changed-blocks &lt;span class="nt"&gt;--first-snapshot-id&lt;/span&gt; &lt;span class="nv"&gt;$FIRST&lt;/span&gt; &lt;span class="nt"&gt;--second-snapshot-id&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; | jq &lt;span class="s1"&gt;'.ChangedBlocks | length'&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done
&lt;/span&gt;snap-02b0b0a4ba1aa52a4:0
snap-0b35d8d888956aabc:0
snap-02438036484680c4d:0
snap-0b24676f259ea425c:0
snap-02356588643dade8c:0
snap-05391e8ce8e97ac09:0
snap-022654ea634922940:0
snap-0c559252bb1323192:0
snap-0197cfa3b57dd112a:0
snap-0baf04b1bc66d9fbe:0
snap-0c8139bca34c3fa2b:0
snap-01a13015e939ec730:0
snap-03c616d28c4c2f8e2:0
snap-00d3d9a860deebf38:0
snap-0e98d71bec134ca26:0
snap-0374d3f4ce87bad9e:0
snap-0f797d4836e05681d:0
snap-0b305c3b79e375f36:0
snap-06338105ef6571bee:0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mostrando sem o jq:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
&lt;span class="c"&gt;# Saída sem o jq:&lt;/span&gt;
snap-02b0b0a4ba1aa52a4:&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"ChangedBlocks"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;,
    &lt;span class="s2"&gt;"ExpiryTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2024-02-15T22:16:59.026000-03:00"&lt;/span&gt;,
    &lt;span class="s2"&gt;"VolumeSize"&lt;/span&gt;: 8000,
    &lt;span class="s2"&gt;"BlockSize"&lt;/span&gt;: 524288,
    &lt;span class="s2"&gt;"NextToken"&lt;/span&gt;: &lt;span class="s2"&gt;"ADEDAao20vaMS6MgIahZ7GoSz9o7FPruoV9E7tDE4qoDE3YldGqcD84P8/h4vpdT5TY/6I/kb8Ud"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou seja, esses snapshots definitivamente não estão ocupando 800TiB, ou o custo seria na casa de 55 mil dólares!&lt;/p&gt;

&lt;h2&gt;
  
  
  EBS Direct API
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-accessing-snapshot.html"&gt;Este recurso&lt;/a&gt; não tão conhecido é a API pela qual podemos disparar a criação de snapshots.&lt;/p&gt;

&lt;p&gt;Só que vai muito além! Com ela, é possível &lt;strong&gt;ler e escrever dados em snapshots&lt;/strong&gt;, além de analisar as diferenças entre snapshots da mesma origem!&lt;/p&gt;

&lt;h2&gt;
  
  
  Nota de rodapé pensando em segurança
&lt;/h2&gt;

&lt;p&gt;Eu naturalmente estava interessado apenas na funcionlaidade de listar as diferenças entre snapshots. Mas vai aqui algumas informações importantes sobre este serviço sob a ótica da segurança.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ebs/index.html"&gt;&lt;strong&gt;Na documentação&lt;/strong&gt;&lt;/a&gt; é possível consultar os comandos da EBS Direct API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start-snapshot&lt;/li&gt;
&lt;li&gt;complete-snapshot&lt;/li&gt;
&lt;li&gt;get-snapshot-block&lt;/li&gt;
&lt;li&gt;list-changed-blocks&lt;/li&gt;
&lt;li&gt;list-snapshot-blocks&lt;/li&gt;
&lt;li&gt;put-snapshot-block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você olhar o site, eu mudei a ordem propositalmente.&lt;/p&gt;

&lt;p&gt;Os dois primeiros são considerados &lt;strong&gt;Management Events&lt;/strong&gt; pela API da AWS, e portanto são logados por padrão pelo CloudTrail.&lt;/p&gt;

&lt;p&gt;Os outros quatro, entretanto, são considerados &lt;strong&gt;Data Events&lt;/strong&gt;. E como você bem sabe (provavelmente porque já precisou ir atrás de quem deletou algo de um bucket S3), este tipo de evento &lt;strong&gt;não é logado por padrão no CloudTrail&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Ou seja, alguém pode usar &lt;strong&gt;get-snapshot-block&lt;/strong&gt; para baixar pedaços dos seus discos e você &lt;strong&gt;nunca vai saber&lt;/strong&gt; a menos que tenha data events ativado em uma Trail! Maneira extremamente maliciosa de surrupiar dados dos outros.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>ebs</category>
      <category>cloudops</category>
    </item>
    <item>
      <title>Usando cURL para diagnóstico de problemas</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Wed, 20 Dec 2023 01:53:32 +0000</pubDate>
      <link>https://dev.to/marcelo_devsres/usando-curl-para-diagnostico-de-problemas-5d73</link>
      <guid>https://dev.to/marcelo_devsres/usando-curl-para-diagnostico-de-problemas-5d73</guid>
      <description>&lt;p&gt;"O sistema está lento".&lt;/p&gt;

&lt;p&gt;Que sysadmin/devops/SRE (ou seja lá qual o branding moderno para o responsável pela infra) não odeia essa declaração, porque ela não te diz absolutamente nada.&lt;/p&gt;

&lt;p&gt;A aplicação está lenta? Ou é o servidor de aplicação? Ou o balanceador? Ou o storage? &lt;/p&gt;

&lt;p&gt;E se a aplicação for distribuída, o problema fica 100 vezes maior.&lt;/p&gt;

&lt;p&gt;A maneira moderna de fazer diagnóstico deste tipo de problema é usar &lt;strong&gt;Application Performance Monitoring&lt;/strong&gt;, ou monitoramento de performance de aplicações, geralmente caríssimas, para entregar a resposta que você quer.&lt;/p&gt;

&lt;p&gt;Mas o assunto não é este hoje; a ideia é tentar usar o &lt;strong&gt;cURL&lt;/strong&gt; para identificar certos problemas de maneira mais ágil.&lt;/p&gt;

&lt;h2&gt;
  
  
  curl custom output
&lt;/h2&gt;

&lt;p&gt;Você certamente está acostumado a usar cURL para testar conectividade com um site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Qual a versão do kubectl mais nova?&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; https://dl.k8s.io/release/stable.txt
v1.29.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou ainda baixar um programa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Vamos baixar o kubectl!&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; &lt;span class="s2"&gt;"https://dl.k8s.io/release/&lt;/span&gt;&lt;span class="nv"&gt;$LATEST&lt;/span&gt;&lt;span class="s2"&gt;/bin/linux/amd64/kubectl"&lt;/span&gt;
...

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; kubectl
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 marcelo marcelo 49704960 Dec 19 20:30 kubectl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas é possível customizar a saída com diversas coisas. A que mais uso é, de longe, a resposta HTTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl www.google.com &lt;span class="nt"&gt;-so&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}\n'&lt;/span&gt;
200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uma maneira extremamente ineficiente de testar se uma aplicação está com problemas é disparar uma rajada de curl sobre ela e analisar a saída:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..100&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do 
  &lt;/span&gt;curl &lt;span class="nt"&gt;-so&lt;/span&gt; /dev/null 127.0.0.1 &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}\n'&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;
     83 200
      3 500
      5 503
      9 504
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como eu falei, é uma maneira extremamente ineficiente - você se daria melhor executando testes com uma ferramenta de verdade. Mas quando você está on-call com notebook e te acionam...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://everything.curl.dev/usingcurl/verbose/writeout"&gt;&lt;strong&gt;Este link&lt;/strong&gt;&lt;/a&gt; tem as variáveis que podem ser usadas juntamente com o programa &lt;strong&gt;--write-out&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variáveis de writeout para exibir tempos
&lt;/h2&gt;

&lt;p&gt;Suponha que você queira saber quanto tempo um download demora, muita gente gosta de usar o comando &lt;strong&gt;time&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ LATEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt; curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; https://dl.k8s.io/release/stable.txt &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;curl &lt;span class="nt"&gt;-sLO&lt;/span&gt; &lt;span class="s2"&gt;"https://dl.k8s.io/release/&lt;/span&gt;&lt;span class="nv"&gt;$LATEST&lt;/span&gt;&lt;span class="s2"&gt;/bin/linux/amd64/kubectl"&lt;/span&gt;

real    0m0.523s
user    0m0.132s
sys     0m0.141s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas a melhor maneira é pedir para o próprio cURL trazer esta métrica!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ LATEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt; curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; https://dl.k8s.io/release/stable.txt &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sLO&lt;/span&gt;  &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{time_total}\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"https://dl.k8s.io/release/&lt;/span&gt;&lt;span class="nv"&gt;$LATEST&lt;/span&gt;&lt;span class="s2"&gt;/bin/linux/amd64/kubectl"&lt;/span&gt;
0.441882
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A parte interessante é que existem &lt;strong&gt;muitas variáveis diferentes de tempo&lt;/strong&gt; para usar, o que pode ser um excelente recurso na depuração de certos problemas!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.josephscott.org/2011/10/14/timing-details-with-curl/"&gt;Este blog&lt;/a&gt; teve a ideia legal de criar um arquivo com o seguinte formato:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl-format.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
time_namelookup: %{time_namelookup}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
time_connect: %{time_connect}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
time_appconnect: %{time_appconnect}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
time_pretransfer: %{time_pretransfer}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
time_redirect: %{time_redirect}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
time_starttransfer: %{time_starttransfer}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
———&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
time_total: %{time_total}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E agora podemos passar este arquivo para o cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"@curl-format.txt"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; http://devsres.com

time_namelookup: 0.000964
time_connect: 0.002114
time_appconnect: 0.000000
time_pretransfer: 0.002148
time_redirect: 0.000000
time_starttransfer: 0.010617
———
time_total: 0.010684
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Genial, não é?&lt;/p&gt;

&lt;p&gt;Bem, para achar genial, você precisa saber para que serve cada etapa e como usá-las em diagnóstico.&lt;/p&gt;

&lt;p&gt;Vou usar o primeiro, &lt;strong&gt;time_namelookup&lt;/strong&gt; apenas como exemplo!&lt;/p&gt;

&lt;h3&gt;
  
  
  time_namelookup
&lt;/h3&gt;

&lt;p&gt;O poder de diagnóstico desta diretiva não pode ser mensurado!&lt;/p&gt;

&lt;p&gt;Problemas de DNS tendem a ser de difícil diagnóstico para pessoas sem background de redes. Mas observe o seguinte teste em uma máquina que está "consistentemente lenta":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"@curl-format.txt"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; http://devsres.com
time_namelookup: 5.139453
time_connect: 5.156280
time_appconnect: 0.000000
time_pretransfer: 5.156360
time_redirect: 0.000000
time_starttransfer: 5.172965
———
time_total: 5.173074
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apenas para referência, estes são os tempos esperados em um dia normal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"@curl-format.txt"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; http://devsres.com
time_namelookup: 0.046703
time_connect: 0.047609
time_appconnect: 0.000000
time_pretransfer: 0.047647
time_redirect: 0.000000
time_starttransfer: 0.050434
———
time_total: 0.050491
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sabe porque o primeiro teste levou mais de 5 segundos? Porque esse é o &lt;strong&gt;tempo padrão&lt;/strong&gt; que o &lt;strong&gt;linux resolver&lt;/strong&gt; espera para testar a resolução usando o seu servidor secundário!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(man resolv.conf)&lt;br&gt;
&lt;strong&gt;timeout:n&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sets  the  amount of time the resolver will wait for a response from a remote name server before retrying the query via a different name server. This may not be the total time taken by any resolver API call and there is no guarantee that a single resolver API call maps to a single timeout.  Measured in seconds, the default is RES_TIMEOUT (currently 5, see ). The value for this option is silently capped to 30.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Configurar múltiplos servidores DNS no seu /etc/resolv.conf é útil para garantir resiliência, mas o ambiente ficará &lt;strong&gt;severamente degradado&lt;/strong&gt; caso o primeiro servidor falhe se precisar resolver nomes para seu funcionamento!&lt;/p&gt;

&lt;p&gt;É possível reduzir esse tempo de timeout com a seguinte diretiva em seu resolv.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf
options &lt;span class="nb"&gt;timeout&lt;/span&gt;:1
nameserver 192.168.0.53
nameserver 1.1.1.1
nameserver 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A configuração entra em vigor imediatamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"@curl-format.txt"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; http://devsres.com
time_namelookup: 1.115452
time_connect: 1.135235
time_appconnect: 0.000000
time_pretransfer: 1.135265
time_redirect: 0.000000
time_starttransfer: 1.151118
———
time_total: 1.151194
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se não funcionar, talvez seja necessário ajudar esse parâmetro usando &lt;strong&gt;network manager&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Neste caso, usamos este parâmetro para permitir identificar que a causa do problema é o não funcionamento do servidor DNS do cliente, e não a aplicação! É um excelente primeiro teste a fazer para identificar se realmente há um problema no seu ambiente, antes de fazer dezenas de verificações e constatar que está tudo ok!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Como o RSYNC funciona?</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Sun, 17 Dec 2023 19:01:25 +0000</pubDate>
      <link>https://dev.to/marcelo_devsres/como-o-rsync-funciona-3lib</link>
      <guid>https://dev.to/marcelo_devsres/como-o-rsync-funciona-3lib</guid>
      <description>&lt;p&gt;Em um determinado projeto esta semana, comentei que o clássico programa &lt;strong&gt;rsync&lt;/strong&gt;, na verdade, &lt;strong&gt;não faz análise de checksum&lt;/strong&gt; por padrão na hora de identificar que arquivos transferir. Isso surpreendeu algumas pessoas!&lt;/p&gt;

&lt;p&gt;Então acredito que compartilhar este "mini-lab" com explicações ajude você a entender o básico do funcionamento do rsync!&lt;/p&gt;

&lt;h2&gt;
  
  
  RSYNC é assunto para PHD!
&lt;/h2&gt;

&lt;p&gt;O Tridgell , um dos criadores do &lt;strong&gt;rsync&lt;/strong&gt;, descreveu o funcionamento do programa em sua tese de Ph.D. em 1999, e você pode lê-la &lt;a href="https://www.samba.org/~tridge/phd_thesis.pdf"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Como nós não somos acadêmicos aqui e queremos apenas usar a ferramenta, vamos decompor duas partes importantes do funcionando do rsync:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Como ele determina &lt;strong&gt;que arquivos precisam ser enviados para o destino&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;Quais as &lt;strong&gt;partes do arquivo foram alteradas&lt;/strong&gt; e &lt;strong&gt;precisam ser enviadas para o destino&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;São duas coisas totalmente diferentes, mas que geram a confusão que eu entendo que muitas pessoas fazem.&lt;/p&gt;

&lt;p&gt;Analisando apenas a parte 2., o rsync é capaz de, a partir de um arquivo com determinado tamanho, dividí-lo em pedaços, calcular um hash para cada pedaço, e identificar qual pedaço precisa ser enviado. Isso é excepcionalmente útil na hora de transferir um arquivo muito grande mas que é 99,9% igual na origem ou no destino. Um exemplo seriam arquivos de logs gerados de maneira progressiva ou massas de dados incompletas que foram transferidas parcialmente por interrupção do serviço. Nessas horas, você pode contar com o rsync para transferir apenas o incremento de 1 ou 2 GiB, e não os 10TiB exatamente iguais do arquivo.&lt;/p&gt;

&lt;p&gt;Mas essa operação de checksum é processada &lt;strong&gt;após o rsync entender que o arquivo precisa ser enviado&lt;/strong&gt;. Isso &lt;strong&gt;não quer dizer que o rsync executa um checksum em cada arquivo para saber que ele precisa ser alterado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;É extremamente importante &lt;strong&gt;não confundir as duas coisas&lt;/strong&gt;, são etapas diferentes!&lt;/p&gt;

&lt;h2&gt;
  
  
  Que arquivos precisam ser enviados para o destino?
&lt;/h2&gt;

&lt;p&gt;A resposta está no &lt;code&gt;man rsync&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rsync  finds  files  that  need to be transferred using a "quick check" algorithm (by default) that looks for files that have changed in size or in last-modified time.  Any changes in the other preserved attributes (as requested by options) are  made  on  the destination file directly when the quick check indicates that the file's data does not need to be updated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Então, o "algoritmo" padrão de detecção nem merece um nome bonito, chamado de &lt;strong&gt;quick check&lt;/strong&gt;. Ele observa &lt;strong&gt;mudanças no tamanho&lt;/strong&gt; do arquivo ou na &lt;strong&gt;data e hora da última modificação&lt;/strong&gt; (ou &lt;strong&gt;mtime&lt;/strong&gt;). &lt;/p&gt;

&lt;p&gt;Algumas pessoas chamam esse algoritmo de &lt;strong&gt;checkrq&lt;/strong&gt; ou &lt;strong&gt;lquick&lt;/strong&gt; ou ainda pior &lt;strong&gt;lquickcheckrq&lt;/strong&gt; por causa &lt;a href="https://linux.die.net/man/1/rsync"&gt;desta página&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GuVAb8JF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/radwnsxaxczvxvqad9ko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GuVAb8JF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/radwnsxaxczvxvqad9ko.png" alt="Screenshot do Linux Die" width="477" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isso inclusive uma grande piada interna de alguns usuários mais atentos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--atWdWcsG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mo0sk67p0u5oo8n3gvv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--atWdWcsG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mo0sk67p0u5oo8n3gvv2.png" alt="Screeshot da rsync manpage" width="491" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;lq&lt;/strong&gt; do 'lqquick' e o &lt;strong&gt;rq&lt;/strong&gt; do 'checkrq' são, na verdade, o "left quote" e "right quote" da manpage mal transpostos do formato man para html! Logo, por favor, não chame o algoritmo de "checkrq" ou você corre o risco de alguém gargalhar na sua frente!&lt;/p&gt;

&lt;p&gt;Mas a parte importante é essa: rsync, por padrão, &lt;strong&gt;não executa checksum nos arquivos para decidir se ele precisa transferí-lo&lt;/strong&gt; (inteiro ou seus pedaços) ou não. Ele executa uma simples análise de timestamp e tamanho.&lt;/p&gt;

&lt;p&gt;Por quê? Porque é muito mais rápido e extremamente efetivo! Qual a chance de um arquivo com mesmo tamanho e mesmo mtime ser diferente em duas origens que comumente se sincronizam?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enganando o rsync:
&lt;/h2&gt;

&lt;p&gt;Vamos fazer um lab.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/testersync/&lt;span class="o"&gt;{&lt;/span&gt;origem,destino&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"O rsync nao faz checksum por padrao"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/testersync/origem/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se, um tempo depois, você criar um arquivo igual no destino:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"O rsync nao faz checksum por padrao"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/testersync/destino/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E usar o rsync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avi&lt;/span&gt; /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;f..t...... arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse t na saída do log mostra que ele notou que o timestamp está diferente.&lt;/p&gt;

&lt;p&gt;Se você mudar o tamanho da frase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Claro que o rsync faz checksum por padrao, oras"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/testersync/destino/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora o rsync vai falar que ele mudou não só o timestamp mas o tamanho:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avi&lt;/span&gt;  /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;f.st...... arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos tentar enganar o rsync. Vou criar o arquivo com a mesma frase, mas toda em maiúscula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/testersync/origem/arquivo | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[a-z]'&lt;/span&gt; &lt;span class="s1"&gt;'[A-Z]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/testersync/destino/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do ponto de vista computacional, claramente eles são diferentes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /tmp/testersync &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt; &lt;span class="se"&gt;\+&lt;/span&gt;
f27d88d52759a4b07c077f34c230c47b94ea88b6640fc47b44d562243966eb7e  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O tamanho é o mesmo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /tmp/testersync &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%P: %s\n'&lt;/span&gt;
destino/arquivo: 36
origem/arquivo: 36
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas se você usar o rsync, ele vai retransferir o arquivo porque o &lt;strong&gt;mtime&lt;/strong&gt; é diferente, e isso é um grande indicador que algo mudou no arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /tmp/testersync &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%P: %t\n'&lt;/span&gt;
destino/arquivo: Sun Dec 17 14:45:20.4011998470 2023
origem/arquivo: Sun Dec 17 14:43:11.4812008470 2023
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Então, se você executar o rsync, ele vai substituir o arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avi&lt;/span&gt; /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;f..t...... arquivo

find /tmp/testersync &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt; &lt;span class="se"&gt;\+&lt;/span&gt;
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Só que isso é bem diferente de executar análise de checksum, que em larga escala, pode ser muito ineficiente. &lt;/p&gt;

&lt;p&gt;Vamos enganar o rsync. Primeiro recrio o arquivo diferente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/testersync/origem/arquivo | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'[a-z]'&lt;/span&gt; &lt;span class="s1"&gt;'[A-Z]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/testersync/destino/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois eu altero os horários dele para ficarem iguais ao da origem (conhece essa função do comando touch?):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /tmp/testersync/destino/arquivo &lt;span class="nt"&gt;-r&lt;/span&gt; /tmp/testersync/origem/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como o rsync enxerga o arquivo agora?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /tmp/testersync &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%P:Tamanho %s, Mtime %t\n'&lt;/span&gt;
destino/arquivo:Tamanho 36, Mtime Sun Dec 17 14:43:11.4812008470 2023
origem/arquivo:Tamanho 36, Mtime Sun Dec 17 14:43:11.4812008470 2023
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do ponto de vista do rsync, eles agora são iguais. Duvida? Vamos executá-lo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avi&lt;/span&gt; /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list

sent 85 bytes  received 12 bytes  194.00 bytes/sec
total size is 36  speedup is 0.37
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pelo log, você pode ver que ele não transferiu nada, e o checksum segue diferente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /tmp/testersync &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt; &lt;span class="se"&gt;\+&lt;/span&gt;
f27d88d52759a4b07c077f34c230c47b94ea88b6640fc47b44d562243966eb7e  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usando checksum para identificar arquivos diferentes
&lt;/h2&gt;

&lt;p&gt;Neste tipo de situação - que geralmente é improvável de acontecer, mas não necessariamente impossível, você precisa da flag -c:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avic&lt;/span&gt; /tmp/testersync/origem/ /tmp/testersync/destino
sending incremental file list
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;fc........ arquivo


find /tmp/testersync &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt; &lt;span class="se"&gt;\+&lt;/span&gt;
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/destino/arquivo
47c6ec4243572212a13d657637a62975a1007cf5881668cf7d02d370956dc3b3  /tmp/testersync/origem/arquivo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora sim, eu garanto que origem e destino são exatamente iguais.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Se você quiser garantir que a origem e o destino são iguais, você precisa &lt;strong&gt;modificar o comportamento padrão do rsync&lt;/strong&gt; usando a flag -c ou --checksum.&lt;/p&gt;

&lt;p&gt;Se você está trabalhando com arquivos que usam algum tipo de empacotamento binário de data (basicamente qualquer coisa), a chance desse tipo de situação passar batido é extremamente baixa. &lt;/p&gt;

&lt;p&gt;Mas quando você trabalha com muitos arquivos textos com formatos e layouts parecidos e com filesystems extravagantes que podem não expor o mtime adequadamente, você pode cair nessa armadilha.&lt;/p&gt;

&lt;p&gt;Em uma era em que a leitura dinâmica e o aprendizado resumido estilo TL;DR segue em voga, muitas vezes vale a pena você parar para entender com calma as ferramentas que você usa para evitar cair em armadilhas no futuro!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>GCP OSLogin: entendendo como funciona!</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Thu, 14 Dec 2023 05:29:04 +0000</pubDate>
      <link>https://dev.to/marcelo_devsres/gcp-oslogin-entendendo-como-funciona-5bl7</link>
      <guid>https://dev.to/marcelo_devsres/gcp-oslogin-entendendo-como-funciona-5bl7</guid>
      <description>&lt;p&gt;Você usa / trabalha com Linux há anos diariamente, e usa SSH para tudo. Provavelmente vai ser difícil apresentar algo que você não conheça, certo?&lt;/p&gt;

&lt;p&gt;Então vamos tirar isso à prova fazendo um estudo de caso do recurso &lt;strong&gt;OSLogin&lt;/strong&gt; do &lt;strong&gt;Google Cloud&lt;/strong&gt;. No final, me conte se aprendeu algo diferente ou não!&lt;/p&gt;

&lt;h2&gt;
  
  
  OS Login
&lt;/h2&gt;

&lt;p&gt;De acordo com a &lt;a href="https://cloud.google.com/compute/docs/oslogin"&gt;documentação&lt;/a&gt;, &lt;strong&gt;OS Login&lt;/strong&gt; é um recurso para melhor gerir o acesso via SSH às suas máquinas virtuais Linux no GCP. Ele faz mais que gerenciar chaves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;é um mecanismo completamente integrado ao &lt;strong&gt;GCP IAM&lt;/strong&gt;, o que garante controle de acesso e permissões granulares usando políticas globais da conta e não do Linux;&lt;/li&gt;
&lt;li&gt;implementa componentes que permitem ao Linux reconhecer identidades externas, que podem estar em um servidor LDAP ou no seu Active Directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eu vejo muitas comparações com o &lt;a href="https://docs.aws.amazon.com/pt_br/systems-manager/latest/userguide/session-manager.html"&gt;&lt;strong&gt;AWS Systems Manager - Session Manager&lt;/strong&gt;&lt;/a&gt;, mas é um recurso muito mais parecido com outro recurso da AWS chamado &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-eic.html"&gt;&lt;strong&gt;Instance Connect&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como acessar VMs
&lt;/h2&gt;

&lt;p&gt;Em uma instância com este recurso ativado (é possível ativá-lo na maioria das instâncias Linux no processo de criação), basta executar um único comando que você consegue o acesso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute ssh instancia-publica
...
Last login: Thu Dec 14 02:20:56 2023 from 35.235.244.32
cloud_user_p_0fffe37e_linuxacade@instancia-publica:~&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se sua máquina &lt;strong&gt;não&lt;/strong&gt; tiver um endereço IP público para acesso direto, o &lt;strong&gt;Gcloud&lt;/strong&gt; é esperto o suficiente para saber que &lt;a href="https://cloud.google.com/iap/docs/using-tcp-forwarding"&gt;precisa de um passo a mais para viabilizar&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute ssh instance-2
External IP address was not found&lt;span class="p"&gt;;&lt;/span&gt; defaulting to using IAP tunneling.
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usuários com a role &lt;strong&gt;compute.osLogin&lt;/strong&gt; ou &lt;strong&gt;compute.osAdminLogin&lt;/strong&gt; naquela ainstância poderão acessar a máquina. Pode ser necessário mais permissões caso a máquina use uma ServiceAccount.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funciona o OS Login - cliente SSH
&lt;/h2&gt;

&lt;p&gt;Ao tentar usar OS Login a primeira vez, você deverá receber a seguinte mensagem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute ssh instance-1
WARNING: The private SSH key file &lt;span class="k"&gt;for &lt;/span&gt;gcloud does not exist.
WARNING: The public SSH key file &lt;span class="k"&gt;for &lt;/span&gt;gcloud does not exist.
WARNING: You &lt;span class="k"&gt;do &lt;/span&gt;not have an SSH key &lt;span class="k"&gt;for &lt;/span&gt;gcloud.
WARNING: SSH keygen will be executed to generate a key.
Generating public/private rsa key pair.
Enter passphrase &lt;span class="o"&gt;(&lt;/span&gt;empty &lt;span class="k"&gt;for &lt;/span&gt;no passphrase&lt;span class="o"&gt;)&lt;/span&gt;:
Enter same passphrase again:
Your identification has been saved &lt;span class="k"&gt;in&lt;/span&gt; /home/marcelo/.ssh/google_compute_engine
Your public key has been saved &lt;span class="k"&gt;in&lt;/span&gt; /home/marcelo/.ssh/google_compute_engine.pub
The key fingerprint is:
SHA256:W0xABfw0HgNyBgsPX6av+co9ZM/JWaa893fcIhM3zL8 marcelo@devsres.com
The key&lt;span class="s1"&gt;'s randomart image is:
+---[RSA 3072]----+
|    o o+X+.      |
|     = O..=      |
|      =  +.+     |
|       . oo      |
|        S o  o   |
|       ooo  + =  |
|      oo.= * o +.|
|     . o. O + . *|
|      o.o..o +.Eo|
+----[SHA256]-----+
...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ainda que você tenha chaves - mesmo com os nomes padrão id_rsa, id_ecdsa, id_ed25519 -, ele irá gerar uma nova para você:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; ~/.ssh/google&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nt"&gt;-rw-------&lt;/span&gt; 1 marcelo marcelo 2602 Dec 13 23:38 /home/marcelo/.ssh/google_compute_engine
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 marcelo marcelo  571 Dec 13 23:38 /home/marcelo/.ssh/google_compute_engine.pub
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 marcelo marcelo  218 Dec 13 23:44 /home/marcelo/.ssh/google_compute_known_hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Observe que eles optam por não poluir o seu arquivo &lt;strong&gt;known_hosts&lt;/strong&gt; com as máquinas do GCP, o comando gcloud adiciona os hosts conhecidos em um arquivo próprio, o .ssh/google_compute_known_hosts)&lt;/p&gt;

&lt;p&gt;A chave gerada é uma RSA de 3072 bits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# O comando executado deve ter sido:&lt;/span&gt;
&lt;span class="c"&gt;# ssh-keygen -t rsa -b 3072 ~/.ssh/google_compute_engineq&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-keygen &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/google_compute_engine.pub
3072 SHA256:W0xABfw0HgNyBgsPX6av+co9ZM/JWaa893fcIhM3zL8 marcelo@dominator &lt;span class="o"&gt;(&lt;/span&gt;RSA&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após gerar a chave, o gcloud "sobe" a chave pública para o GCP sem você ver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute os-login ssh-keys list
FINGERPRINT                                                       EXPIRY
eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É possível ver informações sobre a chave executando o comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute os-login ssh-keys describe &lt;span class="nt"&gt;--key&lt;/span&gt; eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
fingerprint: eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
key: ssh-rsa ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCsPAqzEGyyz5ltlhJd1wxSyde2WOkDz4baJkDyB90dXstaVLkrw64LfYTIu0W2gpJOLkzOsUNjhTJl/3Feda+GvTIpNpom7EboCYbNd9ExTWnw2W1d6Y/Inn1vydOHoi1qt7X97QOfg7avDkYHoXhHMxumRetUWxte1FBKZ8se3KAPUH/GCBEB28EO6OOqs89c9MmjxWq9d1l0iMioGzGRkM2DvowhmVe5NwrYZMeSXBd7x0bGkDTpdaT5RFJf1nvBy8a68SQbTGO6d9KsqsGmTyUrUPhylkG+yzG+DW4p+KRsC8M6bXX0J3DCiHA9evLT6MyuTpOJDpxJAb+UAdXNwNn6T154HJZ1s1w4u5ruenAepaXBqI8EzGKBy0VUBObupQZuEMNj7XkKTlF3hgjwDvnMQr0AbDsz7da/uMhcVjoUCGZPRmeYRtCpUaEG453W9ZgNlQYALy18D4NEaJ85XiQQUQWDbBoEUcdl6R0JCIJK6pe4HxTyJT05I/QYNus&lt;span class="o"&gt;=&lt;/span&gt;
  marcelo@devsres.com
name: &lt;span class="nb"&gt;users&lt;/span&gt;/cloud_user_p_0fffe37e@linuxacademygclabs.com/sshPublicKeys/eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É possível reusar suas chaves sem dificuldade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute os-login ssh-keys add &lt;span class="nt"&gt;--key-file&lt;/span&gt; ~/.ssh/id_rsa.pub &lt;span class="nt"&gt;--ttl&lt;/span&gt; 30
...
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute os-login ssh-keys list
FINGERPRINT                                                       EXPIRY
ca30c038ede7cc7ed7bee957d7778988b540f8204beabe2cdccf00ae78031a30  2023-12-14T03:19:19Z
eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Executar o "gcloud compute ssh" especificando uma chave privada sem subir a chave pública faz o auto upload da chave com TTL 0 (ou seja, não expira).&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funciona o OS Login no servidor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  No Linux: NSS
&lt;/h3&gt;

&lt;p&gt;Agora sim, a parte interessante!&lt;/p&gt;

&lt;p&gt;Primeiramente, vamos observar com que usuário você autentica no servidor remoto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute ssh instancia-publica &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;playground-s-11-b0bd65c9 &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-central1-a
...
cloud_user_p_0fffe37e_linuxacade@instance-1:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;whoami
&lt;/span&gt;cloud_user_p_0fffe37e_linuxacade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O usuário está associado ao meu usuário do Google Cloud - que é meio grande e não coube: &lt;strong&gt;&lt;a href="mailto:cloud_user_p_0fffe37e@linuxacademygclabs.com"&gt;cloud_user_p_0fffe37e@linuxacademygclabs.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Aqui a primeira diferença deste recurso para o SSM Session Manager e o Instance Connect: esse usuário &lt;strong&gt;não foi criado localmente na máquina&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;cloud_user /etc/passwd | &lt;span class="nb"&gt;echo &lt;/span&gt;nao achei nada
nao achei nada
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso acontece porque o OS Login integra com uma velha conhecida no mundo Linux, a &lt;strong&gt;libnss&lt;/strong&gt;, por meio de um módulo específico (na verdade, dois):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 13 /etc/nsswitch.conf
&lt;span class="c"&gt;# /etc/nsswitch.conf&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Example configuration of GNU Name Service Switch functionality.&lt;/span&gt;
&lt;span class="c"&gt;# If you have the `glibc-doc-reference' and `info' packages installed, try:&lt;/span&gt;
&lt;span class="c"&gt;# `info libc "Name Service Switch"' for information about this file.&lt;/span&gt;

passwd:         files cache_oslogin oslogin
group:          files cache_oslogin oslogin
shadow:         files
gshadow:        files

hosts:          files dns
networks:       files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O arquivo &lt;strong&gt;nsswitch.conf&lt;/strong&gt; é onde, tradicionalmente, mapeamos essa integração. A resolução de nomes, por exemplo, ocorre na ordem &lt;strong&gt;files&lt;/strong&gt; -&amp;gt; &lt;strong&gt;dns&lt;/strong&gt; (ou seja, /etc/host, depois linux client resolver). Para entradas no &lt;strong&gt;/etc/passwd&lt;/strong&gt; ou &lt;strong&gt;/etc/group&lt;/strong&gt;, a libnss orienta buscar, após destes arquivos,  primeiramente no provider &lt;strong&gt;cache_oslogin&lt;/strong&gt;, e posteriormente no próprio &lt;strong&gt;oslogin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Essas libs são instaladas pelo pacote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;dpkg &lt;span class="nt"&gt;-L&lt;/span&gt; google-compute-engine-oslogin | fgrep libnss
/lib/x86_64-linux-gnu/libnss_cache_oslogin-20231004.00.so
/lib/x86_64-linux-gnu/libnss_oslogin-20231004.00.so
/lib/x86_64-linux-gnu/libnss_cache_oslogin.so.2
/lib/x86_64-linux-gnu/libnss_oslogin.so.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Também há &lt;strong&gt;units&lt;/strong&gt; do systemd ativas com estes programas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;systemctl list-units &lt;span class="s1"&gt;'google*'&lt;/span&gt;
  UNIT                            LOAD   ACTIVE SUB     DESCRIPTION
  google-guest-agent.service      loaded active running Google Compute Engine Guest Agent
  google-osconfig-agent.service   loaded active running Google OSConfig Agent
  google-shutdown-scripts.service loaded active exited  Google Compute Engine Shutdown Scripts
  google-oslogin-cache.timer      loaded active waiting NSS cache refresh timer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dependendo do provider, é possível ver as entradas usando o comando abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;getent passwd | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n5&lt;/span&gt;
systemd-timesync:x:999:999:systemd Time Synchronization:/:/usr/sbin/nologin
systemd-coredump:x:998:998:systemd Core Dumper:/:/usr/sbin/nologin
cloud_user_p_0fffe37e_linuxacade:&lt;span class="k"&gt;*&lt;/span&gt;:1862762045:1862762045::/home/cloud_user_p_0fffe37e_linuxacade:/bin/bash
sa_112203697991872690750:&lt;span class="k"&gt;*&lt;/span&gt;:3661484281:3661484281::/home/sa_112203697991872690750:/bin/bash
sa_109344113766727672345:&lt;span class="k"&gt;*&lt;/span&gt;:3686152525:3686152525::/home/sa_109344113766727672345:/bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nem sempre o comando getent traz todas as entradas. Isso não quer dizer que o sistema operacional não seja capaz de enxergá-lo, apenas indica que a biblioteca usada não necessariamente suporta enumeração.&lt;/p&gt;

&lt;p&gt;Se seu usuário não fosse visível para o SO, você não conseguiria nem logar!&lt;/p&gt;

&lt;p&gt;Se ele não surgir na saída do comando acima, é possível fazer uma query direta sobre ele especificando o nome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;id
&lt;/span&gt;&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1862762045&lt;span class="o"&gt;(&lt;/span&gt;cloud_user_p_0fffe37e_linuxacade&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1862762045&lt;span class="o"&gt;(&lt;/span&gt;cloud_user_p_0fffe37e_linuxacade&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1862762045&lt;span class="o"&gt;(&lt;/span&gt;cloud_user_p_0fffe37e_linuxacade&lt;span class="o"&gt;)&lt;/span&gt;,44&lt;span class="o"&gt;(&lt;/span&gt;video&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;getent passwd cloud_user_p_0fffe37e_linuxacade
cloud_user_p_0fffe37e_linuxacade:&lt;span class="k"&gt;*&lt;/span&gt;:1862762045:1862762045::/home/cloud_user_p_0fffe37e_linuxacade:/bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nem todo mundo conhece estas funcionalidades do mundo Linux! Elas vêm desde lá atrás com o NIS/Yellow Pages, e geralmente só quem administra servidores integrados com LDAP ou Active Directory conhece esses recursos!&lt;/p&gt;

&lt;p&gt;Agora, uma outra consideração: as conexões realmente ocorrem via &lt;strong&gt;ssh&lt;/strong&gt; a partir do endereço da sua máquina!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;ss &lt;span class="nt"&gt;-nt&lt;/span&gt; &lt;span class="s1"&gt;'( sport = :22 )'&lt;/span&gt;
State              Recv-Q              Send-Q                           Local Address:Port                            Peer Address:Port              Process
ESTAB              0                   140                                 10.128.0.2:22                             XXX.42.69.YYY:59660
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No caso, o IP &lt;strong&gt;XXX.42.69.YYY&lt;/strong&gt; representa o meu endereço IP. &lt;/p&gt;

&lt;p&gt;Se você executar o comando a partir de um tunel IAP, o endereço vai ser outro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;ss &lt;span class="nt"&gt;-nt&lt;/span&gt; &lt;span class="s1"&gt;'( sport = :22 )'&lt;/span&gt;
State              Recv-Q              Send-Q                           Local Address:Port                           Peer Address:Port               Process
ESTAB              0                   0                                   10.128.0.2:22                             35.235.240.4:46519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou seja, é necessário garantir que haja conectividade a partir dos recursos de Identity Aware Proxy (IAP) do Google Cloud para que isso funcione (i.e., abrir regras para o range &lt;a href="https://cloud.google.com/iap/docs/using-tcp-forwarding#:~:text=allows%20ingress%20traffic%20from%20the,and%20port%203389%20for%20RDP."&gt;35.235.240.0/20&lt;/a&gt;, como descrito na documentação).&lt;/p&gt;

&lt;p&gt;Como os acessos ocorrem &lt;strong&gt;realmente via SSH&lt;/strong&gt; (diferentemente do AWS SSM Session Manager), é necessário configurar um &lt;strong&gt;outro componente&lt;/strong&gt; para viabilizar o login no Linux: o &lt;strong&gt;PAM&lt;/strong&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  No Linux: PAM
&lt;/h3&gt;

&lt;p&gt;O diretório &lt;strong&gt;/etc/pam.d&lt;/strong&gt; conta com a configuração de como vários programas do Linux realizam seu processo de autenticação e estabelecimento de sessões!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /etc/pam.d
chfn      chsh            common-auth      common-session                 cron   newusers  passwd   runuser-l  su    &lt;span class="nb"&gt;sudo
&lt;/span&gt;chpasswd  common-account  common-password  common-session-noninteractive  login  other     runuser  sshd       su-l
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O PAM oculta diversas camadas de complexidade por trás de sua aparente simplicidade, e é imprescindível que um administrador Linux entenda do que se trata e saiba ler o que dizem as configurações.&lt;/p&gt;

&lt;p&gt;Esta é a configuração PAM do comando &lt;strong&gt;sudo&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;egrep &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'^$|^#'&lt;/span&gt;  /etc/pam.d/sudo
@include common-auth
@include common-account
@include common-session-noninteractive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta, a do &lt;strong&gt;su&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;egrep &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'^$|^#'&lt;/span&gt;  /etc/pam.d/su
auth          sufficient pam_rootok.so
session       required   pam_env.so &lt;span class="nv"&gt;readenv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
session       required   pam_env.so &lt;span class="nv"&gt;readenv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;envfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/default/locale
session       optional   pam_mail.so nopen
session       required   pam_limits.so
@include common-auth
@include common-account
@include common-session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E esta a do &lt;strong&gt;ssh&lt;/strong&gt; (&lt;strong&gt;se&lt;/strong&gt; a diretiva &lt;strong&gt;UsePAM&lt;/strong&gt; estiver configurada!):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fgrep &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'UsePAM'&lt;/span&gt;  /etc/ssh/sshd_config
UsePAM &lt;span class="nb"&gt;yes&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;egrep &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'^$|^#'&lt;/span&gt;  /etc/pam.d/sshd
auth       &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore] pam_group.so
@include common-auth
account    required     pam_nologin.so
@include common-account
session &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ok &lt;span class="nv"&gt;ignore&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore &lt;span class="nv"&gt;module_unknown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bad]        pam_selinux.so close
session    required     pam_loginuid.so
session    optional     pam_keyinit.so force revoke
@include common-session
session    optional     pam_motd.so  &lt;span class="nv"&gt;motd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/motd.dynamic
session    optional     pam_motd.so noupdate
session    optional     pam_mail.so standard noenv &lt;span class="c"&gt;# [1]&lt;/span&gt;
session    required     pam_limits.so
session    required     pam_env.so &lt;span class="c"&gt;# [1]&lt;/span&gt;
session    required     pam_env.so &lt;span class="nv"&gt;user_readenv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;envfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/default/locale
session &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ok &lt;span class="nv"&gt;ignore&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore &lt;span class="nv"&gt;module_unknown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bad]        pam_selinux.so open
@include common-password
session    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;success&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ok &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignore] pam_mkhomedir.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Achou complicado? Note que em todos há &lt;strong&gt;includes&lt;/strong&gt; de outros arquivos, o que 'empilha' as diretivas com as descritas nesses arquivos! Isso existe para evitar que você precise reconfigurar cada programa separadamente com as mesmas diretivas!&lt;/p&gt;

&lt;p&gt;O Google OS Login pode modificar o PAM e usar dois módulos, o &lt;strong&gt;pam_oslogin_login.so&lt;/strong&gt; para determinar se o usuário pode autenticar, e o &lt;strong&gt;pam_oslogin_admin.so&lt;/strong&gt; que determina se o usuário vai poder usar o comando &lt;strong&gt;sudo&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Em vez de criar as entradas em /etc/sudoers.d, o OS Login deixa um arquivo com um include para outro diretório:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/sudoers.d/google-oslogin
&lt;span class="c"&gt;#includedir /var/google-sudoers.d&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /var/google-sudoers.d/&lt;span class="k"&gt;*&lt;/span&gt;
cloud_user_p_0fffe37e_linuxacade &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  No SSH
&lt;/h3&gt;

&lt;p&gt;A parte mais interessante, entretanto, vem nas modificações do SSH. O programa faz as seguintes modificações no arquivo /etc/sshd:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n7&lt;/span&gt; /etc/ssh/sshd_config
&lt;span class="c"&gt;#### Google OS Login control. Do not edit this section. ####&lt;/span&gt;
TrustedUserCAKeys /etc/ssh/oslogin_trustedca.pub
AuthorizedPrincipalsCommand /usr/bin/google_authorized_principals %u %k
AuthorizedPrincipalsCommandUser root
AuthorizedKeysCommand /usr/bin/google_authorized_keys
AuthorizedKeysCommandUser root
&lt;span class="c"&gt;#### End Google OS Login control section. ####&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As três primeiras diretivas estão relacionadas ao uso de &lt;a href="https://goteleport.com/blog/how-to-ssh-properly/"&gt;&lt;strong&gt;certificados SSH&lt;/strong&gt;&lt;/a&gt;, o que, a princípio, não é a funcionalidade usada para as conexões do usuário - mas que vale a leitura a respeito se você nunca ouviu falar!&lt;/p&gt;

&lt;p&gt;A diretiva mais importante para o uso desta funcionalidade é &lt;strong&gt;AuthorizedKeysCommand&lt;/strong&gt;: ela permite que o SSH invoque um comando adicional para recuperar quais chaves estão associadas ao usuário tentando logar.&lt;/p&gt;

&lt;p&gt;As chaves estão salvas no GCP (como mostramos na sessão do cliente!); logo, basta que a instância seja capaz de acessar a API do GCP e listar as chaves públicas cadastradas para o usuário!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;/usr/bin/google_authorized_keys cloud_user_p_0fffe37e_linuxacade
ssh-rsa ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCsPAqzEGyyz5ltlhJd1wxSyde2WOkDz4baJkDyB90dXstaVLkrw64LfYTIu0W2gpJOLkzOsUNjhTJl/3Feda+GvTIpNpom7EboCYbNd9ExTWnw2W1d6Y/Inn1vydOHoi1qt7X97QOfg7avDkYHoXhHMxumRetUWxte1FBKZ8se3KAPUH/GCBEB28EO6OOqs89c9MmjxWq9d1l0iMioGzGRkM2DvowhmVe5NwrYZMeSXBd7x0bGkDTpdaT5RFJf1nvBy8a68SQbTGO6d9KsqsGmTyUrUPhylkG+yzG+DW4p+KRsC8M6bXX0J3DCiHA9evLT6MyuTpOJDpxJAb+UAdXNwNn6T154HJZ1s1w4u5ruenAepaXBqI8EzGKBy0VUBObupQZuEMNj7XkKTlF3hgjwDvnMQr0AbDsz7da/uMhcVjoUCGZPRmeYRtCpUaEG453W9ZgNlQYALy18D4NEaJ85XiQQUQWDbBoEUcdl6R0JCIJK6pe4HxTyJT05I/QYNus&lt;span class="o"&gt;=&lt;/span&gt; marcelo@devsres.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso mais uma chave seja adicionada do lado do cliente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute os-login ssh-keys add &lt;span class="nt"&gt;--key-file&lt;/span&gt; ~/.ssh/id_ecdsa.pub &lt;span class="nt"&gt;--ttl&lt;/span&gt; 30
...

&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute os-login ssh-keys list
FINGERPRINT                                                       EXPIRY
68dc496cf5293b7fa094a05b0f0acc92d2382e7bb983a70bacae1ea06cbfaeed  2023-12-14T05:27:22Z
eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A saída do outro comando irá mostrar todas as chaves disponíveis!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;instancia-publica] &lt;span class="nv"&gt;$ &lt;/span&gt;/usr/bin/google_authorized_keys cloud_user_p_0fffe37e_linuxacade
fiXhlbf7uTxcmcYKSrFRakfPRyVtpUVaBLoTJHh70OvITNBXyCiUxwBGM6kKwqSWGX6uD3Bk8nal6cG4eZFHGFy8mfCK3Cx7wNShvXfXEVOqALXJI5W8NPl3eIOMQhx+S1mrK+45K4qTqfaUuq9W8MdrzUEfB4y/DbsgcY4p6GAqTBURuMU0Ym3d+H7DAo4rJCTpy1d/qTvCTkN14Y3slIshmzORmuo2caPpPMsX7iJtpLZkmMJ872gTKU3P5sx7efEv1D89i1WMHFpWAqhIRsKNsJCB53r0Dtt6dK2iKeonWgZYMYfKd+3Px8eDgYyOetNVFhaGXv9w/bDDj60pchENCEDTB59CdAU71tiI/U7738DITq1ZZVzCSWpu0&lt;span class="o"&gt;=&lt;/span&gt; cloud_user_p_0fffe37e@cs-380498931324-default
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCsPAqzEGyyz5ltlhJd1wxSyde2WOkDz4baJkDyB90dXstaVLkrw64LfYTIu0W2gpJOLkzOsUNjhTJl/3Feda+GvTIpNpom7EboCYbNd9ExTWnw2W1d6Y/Inn1vydOHoi1qt7X97QOfg7avDkYHoXhHMxumRetUWxte1FBKZ8se3KAPUH/GCBEB28EO6OOqs89c9MmjxWq9d1l0iMioGzGRkM2DvowhmVe5NwrYZMeSXBd7x0bGkDTpdaT5RFJf1nvBy8a68SQbTGO6d9KsqsGmTyUrUPhylkG+yzG+DW4p+KRsC8M6bXX0J3DCiHA9evLT6MyuTpOJDpxJAb+UAdXNwNn6T154HJZ1s1w4u5ruenAepaXBqI8EzGKBy0VUBObupQZuEMNj7XkKTlF3hgjwDvnMQr0AbDsz7da/uMhcVjoUCGZPRmeYRtCpUaEG453W9ZgNlQYALy18D4NEaJ85XiQQUQWDbBoEUcdl6R0JCIJK6pe4HxTyJT05I/QYNus&lt;span class="o"&gt;=&lt;/span&gt; marcelo@devsres.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É ou não é um recurso interessante?&lt;/p&gt;

&lt;p&gt;Isso porque eu ainda não mostrei a funcionalidade de &lt;a href="https://cloud.google.com/compute/docs/oslogin/set-up-oslogin"&gt;usar SSH com MFA&lt;/a&gt; também disponível!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>EKS and NetworkPolicies: the story so far</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Tue, 12 Sep 2023 00:08:43 +0000</pubDate>
      <link>https://dev.to/aws-builders/eks-and-networkpolicies-the-story-so-far-3f45</link>
      <guid>https://dev.to/aws-builders/eks-and-networkpolicies-the-story-so-far-3f45</guid>
      <description>&lt;p&gt;On my monthly "let´s keep up with AWS news" live stream, one of the news caught my eye as &lt;strong&gt;game changer&lt;/strong&gt;, and it was this one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/08/amazon-vpc-cni-kubernetes-networkpolicy-enforcement/"&gt;Amazon VPC CNI now supports Kubernetes NetworkPolicy enforcement&lt;/a&gt;~&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's have a peek on what this is about and why it is a game changer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes NetworkPolicies
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/"&gt;NetworkPolicies&lt;/a&gt; are often overlooked, but it is basically the major security feature of Kubernetes that grants you the hability to control the conectivity between your applications (and also the rest of the world!).&lt;/p&gt;

&lt;p&gt;Many people get the feeling that the concept of &lt;strong&gt;namespaces&lt;/strong&gt; provides isolation for applications running under, but it &lt;strong&gt;does not provide network isolation by default&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can create a namespace &lt;strong&gt;app1&lt;/strong&gt; and grant access for your &lt;strong&gt;Team A&lt;/strong&gt; to deploy their applications. But if you allow &lt;strong&gt;Team B&lt;/strong&gt; to run &lt;strong&gt;jobs&lt;/strong&gt; on a namespace &lt;strong&gt;app2&lt;/strong&gt;, even if they will not be able to modify app1 deployments, their jobs will be able to connect to the app1 deployments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Checking IPs for the applications:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.status.podIP}{"\n"}{end}'&lt;/span&gt; |
fgrep app
app1/backend-84bf889f7f-nrxbr: 192.168.130.72
app1/frontend-54d8796d8c-2fzsk: 192.168.140.197
app2/job-f8dd4484b-kmxkm: 192.168.138.201

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;192.168.130.72 192.168.140.197&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; app2 &lt;span class="nb"&gt;exec &lt;/span&gt;job-f8dd4484b-kmxkm &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="nt"&gt;-so&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{HTTP_CODE}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="k"&gt;done
&lt;/span&gt;200
200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may not sound like a problem to you, but it is certainly not the most desirable situation, specially if most of your security is invested into the LoadBalancers/Web Application Firewalls that are outside the security perimeter of your cluster - it turns your cluster into a lateral movement extravaganza if someone not intended manages to get access to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing the application
&lt;/h2&gt;

&lt;p&gt;In order to block traffic from other namespaces to your application, you have to apply a &lt;strong&gt;NetworkPolicy&lt;/strong&gt; that denies all incoming traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: app1
spec:
  podSelector: {}
  policyTypes:
  - Ingress
'&lt;/span&gt; | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Syntax might look weird at a glance, but it applies to all pods (podSelector: {}) allowing &lt;strong&gt;nothing&lt;/strong&gt; because there is no &lt;strong&gt;ingress&lt;/strong&gt; block defined at the top level of spec.&lt;/p&gt;

&lt;p&gt;After these rules get applied, no one will be able to access pods on namespace &lt;strong&gt;app1&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;192.168.130.72 192.168.140.197&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; app2 &lt;span class="nb"&gt;exec &lt;/span&gt;job-f8dd4484b-kmxkm &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 2 &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="nt"&gt;-so&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{HTTP_CODE}\n'&lt;/span&gt;
  &lt;span class="k"&gt;done
&lt;/span&gt;000
&lt;span class="nb"&gt;command &lt;/span&gt;terminated with &lt;span class="nb"&gt;exit &lt;/span&gt;code 28
000
&lt;span class="nb"&gt;command &lt;/span&gt;terminated with &lt;span class="nb"&gt;exit &lt;/span&gt;code 28
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problem is &lt;strong&gt;not even applications on app1 will be able to access each other&lt;/strong&gt;. Once you apply any networkpolicy of a type (ingress or egress), all traffic that is not specificly allowed will be &lt;strong&gt;denied&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So if you have a frontend/backend combo like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl -n app1 get pods -o name
pod/backend-84bf889f7f-nrxbr
pod/frontend-54d8796d8c-2fzsk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should allow incoming conections to backend only from the &lt;strong&gt;frontend&lt;/strong&gt; deployment, and frontend might get its connections from a &lt;strong&gt;Load Balancer&lt;/strong&gt; outside the cluster.&lt;/p&gt;

&lt;p&gt;To allow frontend to access backend, one would leverage the &lt;strong&gt;kubernetes labels&lt;/strong&gt; to make the rule &lt;strong&gt;dynamic&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; app1 get pods &lt;span class="nt"&gt;--show-labels&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt;,6 &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;
NAME LABELS
backend-84bf889f7f-nrxbr &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backend,pod-template-hash&lt;span class="o"&gt;=&lt;/span&gt;84bf889f7f
frontend-54d8796d8c-2fzsk &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;frontend,pod-template-hash&lt;span class="o"&gt;=&lt;/span&gt;54d8796d8c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt; pods will always have the label &lt;strong&gt;app=backend&lt;/strong&gt;, and &lt;strong&gt;Frontend&lt;/strong&gt; pods will have the label &lt;strong&gt;app=frontend&lt;/strong&gt;; this will tell the NetworkPolicy Controller to update the rules every time a new pod is created (or destroyed).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Conecting to the **service** backend from the pod frontend&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; app1 &lt;span class="nb"&gt;exec &lt;/span&gt;deploy/frontend &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 2 &lt;span class="nt"&gt;-so&lt;/span&gt; /dev/null http://backend  &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}'&lt;/span&gt;
000
&lt;span class="nb"&gt;command &lt;/span&gt;terminated with &lt;span class="nb"&gt;exit &lt;/span&gt;code 28

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-to-backend
  namespace: app1 
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 80
'&lt;/span&gt; | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -

&lt;span class="c"&gt;# Retesting:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt; kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; app1 &lt;span class="nb"&gt;exec &lt;/span&gt;deploy/frontend &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 2 &lt;span class="nt"&gt;-so&lt;/span&gt; /dev/null http://backend  &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}'&lt;/span&gt;
200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, there is no easy way to restrict access to the frontend pods from the load balancers, because they're not hosted inside the cluster. &lt;/p&gt;

&lt;p&gt;(You can use &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/security-groups-for-pods.html"&gt;Security Groups for Pods&lt;/a&gt; for that!)&lt;/p&gt;

&lt;p&gt;The best you can do is to limit connections from the subnets where the load balancers will create their ENIs (or use their ips, if you feel bold!):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: lb-to-frontend
  namespace: app1
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 192.168.0.0/19
        - ipBlock:
            cidr: 192.168.32.0/19
        - ipBlock:
            cidr: 192.168.64.0/19
      ports:
        - protocol: TCP
          port: 80
'&lt;/span&gt; | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; - 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, anything else that is created on those subnets will be able to access the frontend pods on namespace app1.&lt;/p&gt;

&lt;p&gt;The backend pod, otherwise, won't be able to start any connection to the frontend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;k &lt;span class="nt"&gt;-n&lt;/span&gt; app1 &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; deploy/backend &lt;span class="nt"&gt;--&lt;/span&gt; curl frontend:80 &lt;span class="nt"&gt;-so&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{http_code}'&lt;/span&gt; &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 2
000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kubernetes NetworkPolicies allow an incredible degree of microsegmentation with little to no effort, even allowing for the devs themselves to be responsible to translate their integrations in a declarative way.&lt;/p&gt;

&lt;p&gt;But of course, there is always a catch.&lt;/p&gt;

&lt;h2&gt;
  
  
  NetworkPolicy Controller
&lt;/h2&gt;

&lt;p&gt;Unfortunately, Netpols are one of the few native Kubernetes Resources that &lt;strong&gt;do not have a default controller assigned to it&lt;/strong&gt; - the other major one would be &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/"&gt;Ingresses&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Even though it's clearly stated in the docs, as quoted bellow:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Network policies &lt;strong&gt;are implemented by the network plugin&lt;/strong&gt;. To use network policies, &lt;strong&gt;you must be using a networking solution which supports NetworkPolicy&lt;/strong&gt;. Creating a NetworkPolicy resource &lt;strong&gt;without a controller&lt;/strong&gt; that implements it &lt;strong&gt;will have no effect&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/#prerequisites"&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's still easy for people to miss it, specially if they are less kubernetes-savy than they should be when using this type of technology.&lt;/p&gt;

&lt;p&gt;And yes, I've been asked "why my netpols are not working" before. People just assume that it should work.&lt;/p&gt;

&lt;p&gt;So, if you install an EKS cluster right now and reproduce the same configurations I have listed here, &lt;strong&gt;nothing will work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At least for a while, because things changed!&lt;/p&gt;

&lt;h2&gt;
  
  
  EKS and NetworkPolicies
&lt;/h2&gt;

&lt;p&gt;Until August 2023, the only way use NetworkPolicies was to deploy a third party software called &lt;a href="https://www.tigera.io/project-calico/"&gt;Project Calico&lt;/a&gt;. It's a full fledged CNI, but one would enable only the Policy part as &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/calico.html"&gt;described in the official EKS docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But now, things changed! If you install a new EKS Cluster with the CNI version 1.14.0 or above, &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/08/amazon-vpc-cni-kubernetes-networkpolicy-enforcement/"&gt;it now supports NetworkPolicies natively!&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;All you have to do is to create your EKS Cluster with the following on your eksctl yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
addons:
- name: vpc-cni 
  version: 1.14.0
  configurationValues: |-
    enableNetworkPolicy: &lt;span class="s2"&gt;"true"&lt;/span&gt;    
  attachPolicyARNs:
  - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Awesome news for EKS users!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;I believe it's worth noting that AWS CNI native NetworkPolicies make use of &lt;a href="https://ebpf.io/"&gt;&lt;strong&gt;eBPF&lt;/strong&gt;&lt;/a&gt; and not &lt;strong&gt;iptables&lt;/strong&gt; as many of the other solutions available.&lt;/p&gt;

&lt;p&gt;It does not support port translation from Services:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For any of your Kubernetes services, the service port must be the same as the container port. If you're using named ports, use the same name in the service spec too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a brief period during creation, the pod will not have any restrictions applied to it: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Amazon VPC CNI plugin for Kubernetes configures network policies for pods in parallel with the pod provisioning. Until all of the policies are configured for the new pod, containers in the new pod will start with a default allow policy. All ingress and egress traffic is allowed to and from the new pods unless they are resolved against the existing policies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's also not supported on Fargate or Windows nodes. &lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/08/amazon-vpc-cni-kubernetes-networkpolicy-enforcement/"&gt;https://aws.amazon.com/about-aws/whats-new/2023/08/amazon-vpc-cni-kubernetes-networkpolicy-enforcement/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/containers/amazon-vpc-cni-now-supports-kubernetes-network-policies/"&gt;https://aws.amazon.com/blogs/containers/amazon-vpc-cni-now-supports-kubernetes-network-policies/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Terraform refactoring</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Sat, 08 Oct 2022 03:55:51 +0000</pubDate>
      <link>https://dev.to/marcelo_devsres/terraform-refactoring-1330</link>
      <guid>https://dev.to/marcelo_devsres/terraform-refactoring-1330</guid>
      <description>&lt;p&gt;Estava trabalhando com Terraform gerando dados para testes da nova versão do &lt;a href="https://github.com/netbox-community/netbox"&gt;&lt;strong&gt;Netbox&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Em vez de destruir todos os dados para reexecutar o código Terraform final , aproveitei para fazer uma demonstração de &lt;strong&gt;Terraform Refactoring&lt;/strong&gt; com as diretivas &lt;strong&gt;moved&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contexto
&lt;/h2&gt;

&lt;p&gt;Originalmente, dois recursos &lt;strong&gt;netbox_region&lt;/strong&gt; eram criados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Região de São Paulo&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"netbox_region"&lt;/span&gt; &lt;span class="s2"&gt;"sp"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"São Paulo"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Estado de São Paulo"&lt;/span&gt;
  &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sp"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Região de Brasília&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"netbox_region"&lt;/span&gt; &lt;span class="s2"&gt;"df"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Distrito Federal"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Distrito Federal"&lt;/span&gt;
  &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"df"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado da aplicação do código acima pode ser inspecionado no arquivo de estados do Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state list | fgrep netbox_region
netbox_region.df
netbox_region.sp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Refactor para variáveis e objetos
&lt;/h2&gt;

&lt;p&gt;Não é algo que agrada a todo mundo, mas você pode parametrizar as configurações usando objetos complexos.&lt;/p&gt;

&lt;p&gt;Regra geral, eu &lt;strong&gt;não recomendo&lt;/strong&gt; complexidade excessiva na definição de variáveis porque deixa tudo confuso e difícil para ler ou adaptar posteriormente. Mas vamos lá:&lt;/p&gt;

&lt;p&gt;Podemos deixar o arquivo &lt;strong&gt;variables.tf&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"regions"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
            &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
            &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Estados do Brasil"&lt;/span&gt;

    &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"São Paulo"&lt;/span&gt;
          &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Estado de São Paulo"&lt;/span&gt;
          &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sp"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;df&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Distrito Federal"&lt;/span&gt;
            &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Distrito Federal"&lt;/span&gt;
            &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"df"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;    
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tudo isso me permite substituir os dois recursos &lt;strong&gt;netbox_region&lt;/strong&gt; por um único bloco usando &lt;strong&gt;for_each&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;(Vou ser honesto, não me parece uma troca muito inteligente!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "netbox_region" "estado" {
  for_each = var.regions
  name = each.value.name
  description = each.value.description
  slug = each.value.slug
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviamente, se você fizer apenas isso, vai literalmente destruir ambos os recursos originais para criar dois novos. Dependendo do tipo de API com que o Terraform irá lidar, pode ser que nem seja possível fazer as alterações. &lt;/p&gt;

&lt;p&gt;Neste código, o que acontece é isso aqui:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform plan | egrep &lt;span class="s1"&gt;'Plan|# netbox'&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_region.df will be destroyed&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_region.estado["df"] will be created&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_region.estado["sp"] will be created&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_region.sp will be destroyed&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_site.df will be updated in-place&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_site.df_spc_1 will be updated in-place&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_site.sp will be updated in-place&lt;/span&gt;
  &lt;span class="c"&gt;# netbox_site.sp_spc_1 will be updated in-place&lt;/span&gt;
Plan: 2 to add, 4 to change, 2 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas eu não estou modificando nada, não estou recriando nada, eu quero apenas substituir uma maneira de criar os recursos por outra.&lt;/p&gt;

&lt;p&gt;Tradicionalmente, seria necessário fazer mudanças com o comando &lt;strong&gt;terraform state mv&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state &lt;span class="nb"&gt;mv &lt;/span&gt;netbox_region.sp &lt;span class="s1"&gt;'netbox_region.estado["sp"]'&lt;/span&gt;
Move &lt;span class="s2"&gt;"netbox_region.sp"&lt;/span&gt; to &lt;span class="s2"&gt;"netbox_region.estado[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;sp&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; 
Successfully moved 1 object&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state &lt;span class="nb"&gt;mv &lt;/span&gt;netbox_region.df &lt;span class="s1"&gt;'netbox_region.estado["df"]'&lt;/span&gt;
Move &lt;span class="s2"&gt;"netbox_region.df"&lt;/span&gt; to &lt;span class="s2"&gt;"netbox_region.estado[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;df&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]"&lt;/span&gt; 
Successfully moved 1 object&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;terraform plan 
...
No changes. Your infrastructure matches the configuration.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Desde a versão 1.1, entretanto, temos uma outra maneira mais legal que executar dezenas de comandos: os blocos &lt;strong&gt;moved&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Os blocos abaixo substituem os comandos &lt;strong&gt;terraform state mv&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;moved&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;netbox_region&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;df&lt;/span&gt;
  &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;netbox_region&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;estado&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"df"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;moved&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;netbox_region&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sp&lt;/span&gt;
  &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;netbox_region&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;estado&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com este bloco, o Terraform modifica o state e não faz operações na API remota:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt; plan
...
Terraform will perform the following actions:

  &lt;span class="c"&gt;# netbox_region.df has moved to netbox_region.estado["df"]&lt;/span&gt;
    resource &lt;span class="s2"&gt;"netbox_region"&lt;/span&gt; &lt;span class="s2"&gt;"estado"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;
        name             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Distrito Federal"&lt;/span&gt;
        &lt;span class="c"&gt;# (3 unchanged attributes hidden)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# netbox_region.sp has moved to netbox_region.estado["sp"]&lt;/span&gt;
    resource &lt;span class="s2"&gt;"netbox_region"&lt;/span&gt; &lt;span class="s2"&gt;"estado"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;
        name             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Estao de São Paulo"&lt;/span&gt;
        &lt;span class="c"&gt;# (3 unchanged attributes hidden)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 0 to add, 0 to change, 0 to destroy.

&lt;span class="nv"&gt;$ &lt;/span&gt;terraform apply plan

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 0 added, 0 changed, 0 destroyed.

&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state list | fgrep netbox_region
netbox_region.estado[&lt;span class="s2"&gt;"df"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
netbox_region.estado[&lt;span class="s2"&gt;"sp"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você pode se perguntar: mas para que eu substituiria dois comandos simples por um monte de blocos poluindo meu código Terraform?&lt;/p&gt;

&lt;p&gt;Bem, primeiramente, trabalhar com &lt;strong&gt;blocos de configurações declarativas&lt;/strong&gt; não é apenas temático, mas certamente uma alternativa muito melhor que um conjunto isolados de comandos imperativos.&lt;/p&gt;

&lt;p&gt;Vários comandos imperativos quebram a "atomicidade" das operações, além de ser bem incômodo para execução de rollbacks.&lt;/p&gt;

&lt;p&gt;(Nem sempre será possível contemplar 100% dos casos com blocos &lt;strong&gt;move&lt;/strong&gt;, mas é sempre melhor ter uma alternativa declarativa a não ter nenhuma!)&lt;/p&gt;

&lt;p&gt;Além disso, se você codifica um módulo que é usado por dezenas de instâncias de códigos diferentes, você precisa executar os comandos de &lt;strong&gt;terraform state mv&lt;/strong&gt; em &lt;strong&gt;cada uma das instâncias&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Na maioria dos ambientes, além deste tipo de prática não ser bem vista, normalmente é particularmente difícil de conseguir os acessos a todos os arquivos.&lt;/p&gt;

&lt;p&gt;Ao operar "dentro" de um módulo, agora você consegue abstrair boa parte das dores de cabeça que os usuários dos seus módulos podem ter.&lt;/p&gt;

&lt;p&gt;Se você usa Terraform e pretende continuar usando no futuro, é bem provável que precise destes recursos nos seus códigos!&lt;/p&gt;




&lt;p&gt;A página que fez o anúncio dos blocos &lt;strong&gt;moved&lt;/strong&gt; é &lt;a href="https://www.terraform.io/language/modules/develop/refactoring"&gt;esta aqui&lt;/a&gt; e conta com vários outros exemplos em que as técnicas podem ajudar.&lt;/p&gt;

&lt;p&gt;Também há um tutorial que pode ser seguido &lt;a href="https://developer.hashicorp.com/terraform/tutorials/configuration-language/move-config"&gt;aqui&lt;/a&gt; para ajudar a praticar esta técnica. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Observabilidade com Prometheus</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Sat, 01 Oct 2022 06:53:24 +0000</pubDate>
      <link>https://dev.to/marcelo_devsres/observabilidade-com-prometheus-4dii</link>
      <guid>https://dev.to/marcelo_devsres/observabilidade-com-prometheus-4dii</guid>
      <description>&lt;p&gt;Documentando &lt;a href="https://speakerdeck.com/mrrandrade/observabilidade-episodio-2-metricas" rel="noopener noreferrer"&gt;minha apresentação&lt;/a&gt; criada originalmente para o &lt;a href="https://www.instagram.com/devopsheroes/?hl=en" rel="noopener noreferrer"&gt;DevOps Experience conduzido pelo DevOps Heroes&lt;/a&gt;, aqui vai um resumo sobre métricas no Prometheus!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prometheus
&lt;/h2&gt;

&lt;p&gt;Prometheus como solução de monitoramento é uma "cria" interessante: ele não se limita a um conjunto de ferramentas "server-side" - aliás, o principal foco é justamente a &lt;a href="https://github.com/prometheus/client_golang" rel="noopener noreferrer"&gt;existência&lt;/a&gt; de &lt;a href="https://github.com/prometheus/client_python" rel="noopener noreferrer"&gt;diversas&lt;/a&gt; &lt;a href="https://github.com/prometheus/client_java" rel="noopener noreferrer"&gt;bibliotecas&lt;/a&gt; &lt;a href="https://github.com/prometheus/client_rust" rel="noopener noreferrer"&gt;disponíveis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Inclusive ele mesmo "se vende" como solução para implementar whitebox monitoring:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54huawhison81g4eshzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54huawhison81g4eshzg.png" alt="Prometheus vs Nagions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tipos de métricas
&lt;/h2&gt;

&lt;p&gt;Como descrito &lt;a href="https://www.instagram.com/p/CehrmHIuTo5/" rel="noopener noreferrer"&gt;neste post&lt;/a&gt;, Prometheus disponibiliza quatro tipos de métricas para uso na instrumentação da sua aplicação:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gauges&lt;/li&gt;
&lt;li&gt;Counters &lt;/li&gt;
&lt;li&gt;Histograms&lt;/li&gt;
&lt;li&gt;Summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos entender como cada uma funciona do ponto de vista de objetivos de monitoração.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gauges
&lt;/h2&gt;

&lt;p&gt;Métricas do tipo &lt;strong&gt;Gauge&lt;/strong&gt; é um número que representa um "snapshot" do que está sendo observado.  Memória livre, espaço em disco consumido, uso de CPU, temperatura do datacenter, algo que pode aumentar ou diminuir.&lt;/p&gt;

&lt;p&gt;A maior parte da monitoração tradicional se encaixa com este tipo de métrica. Elas nos permitem formar simpáticos dashboards para acompanhar ao longo do tempo e gerar alertas caso determinada situação persista por muito tempo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Consumo de memória (em %)
node_memory_MemAvailable_bytes / 
node_memory_MemTotal_bytes

# Espaço em disco ocupado (em %^)
(1 - node_filesystem_avail_bytes{mountpoint="/mnt/c"}) / 
     node_filesystem_size_bytes{mountpoint="/mnt/c"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjay37tz7njrl5ou85nu4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjay37tz7njrl5ou85nu4.png" alt="Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ambas as métricas acima foram extraídas usando o &lt;a href="https://github.com/prometheus/node_exporter" rel="noopener noreferrer"&gt;Prometheus Node Exporter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Há uma pequena diferença entre as métricas acima.&lt;/p&gt;

&lt;p&gt;Como há apenas uma única máquina sendo monitorada, não precisei especificar nenhum &lt;strong&gt;filtro de labels&lt;/strong&gt; na consulta. &lt;/p&gt;

&lt;p&gt;Já no espaço em disco, as métricas são exibidas para cada sistema de arquivos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pmazie7phgg027e17rb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pmazie7phgg027e17rb.png" alt="Prometheus filesystems"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No caso, no Dashboard, optei por limitar a métrica ao sistema de arquivos "/mnt/c":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 - node_filesystem_avail_bytes{mountpoint="/mnt/c"} /
    node_filesystem_size_bytes{mountpoint="/mnt/c"} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A terceira métrica é mais interessante: CPU. Ampliando o gráfico:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwslk3mf9czrt25y7giu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwslk3mf9czrt25y7giu.png" alt="Consumo de CPU"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A máquina conta com 8 CPUs - ou o mais provável, 8 &lt;strong&gt;cores&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A métrica para recuperar essa informação é muito mais complexa que as anteriores, e você vai perceber que &lt;strong&gt;eu menti&lt;/strong&gt; ao colocá-la ao lado das duas outras - porque esta &lt;strong&gt;não&lt;/strong&gt; é um &lt;strong&gt;Gauge&lt;/strong&gt; - e o próprio Grafana me dedura:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fztmosuwrpt9ztkfj5eiy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fztmosuwrpt9ztkfj5eiy.png" alt="Selected metric is a counter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Entretanto, ninguém percebeu na apresentação!&lt;/p&gt;

&lt;p&gt;Usei de conhecimento arcano em Prometheus para exibir valores que "sobem" e "descem". Aqui estão os recursos necessários para viabilizar o Dashboard como ele apareceu na imagem anterior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Consumo por CPU
1 - (avg by (cpu) (rate(node_cpu_seconds_total{mode="idle"}[1m])))

# Consumo de CPU agregado 
1 - (avg (rate(node_cpu_seconds_total{mode="idle"}[1m])))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você pode me perguntar: mas por que tão complicado? Não seria bem mais simples coletar um "snapshot" do consumo de cada core em um dado momento e expor esse valor? Por exemplo, no instante da coleta, 4 CPUs estão a 100%, e 4 em 15%.&lt;/p&gt;

&lt;p&gt;Mas pense um pouco. Se você acompanha o consumo de um host por uma amostragem em um dado em um dado momento, quão confiável é aquele valor? Especialmente se o intervalo da amostragem for em períodos longos, como 5 minutos?&lt;/p&gt;

&lt;p&gt;Existe uma razão para o Prometheus fazer uma tarefa aparentemente simples ("eu só quero uma amostra do consumo de CPU") de maneira tão surrealmente complicada. &lt;/p&gt;

&lt;p&gt;A resposta é uma tríplice combinação de &lt;strong&gt;busca por capacidade de inferência&lt;/strong&gt; combinada com a &lt;strong&gt;flexibilidade  e expressividade matemática&lt;/strong&gt; e a &lt;strong&gt;simplicidade de para implementar contadores simples&lt;/strong&gt; ao instrumentar aplicações.&lt;/p&gt;

&lt;p&gt;Já vimos a flexibilidade matemática no exemplo da consulta em PromQL acima. A próxima imagem mostra como a métrica na verdade foi implementada:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93u8wc803602tpktcqxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93u8wc803602tpktcqxp.png" alt="Tempos de CPU"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na verdade, há &lt;strong&gt;8&lt;/strong&gt; métricas geradas para cada core, descrevendo (por meio de um número) o "tempo de cpu" gasto em cada um dos 8 estados (&lt;strong&gt;idle&lt;/strong&gt;, &lt;strong&gt;iowait&lt;/strong&gt;, &lt;strong&gt;irq&lt;/strong&gt;, &lt;strong&gt;nice&lt;/strong&gt;, &lt;strong&gt;softirq&lt;/strong&gt;, &lt;strong&gt;steal&lt;/strong&gt;, &lt;strong&gt;system&lt;/strong&gt; e &lt;strong&gt;user&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Uma ferramenta poderia optar por fazer uma extração de um momento em particular. Ao fazer do "jeito complicado", o Prometheus delega a decisão de como tratar a métrica do lado do servidor, oferecendo flexibilidade para você isolar e analisar os valores como quiser. &lt;/p&gt;

&lt;p&gt;Se você não entendeu a consulta acima, não se preocupe, vem mais por aí! &lt;/p&gt;

</description>
      <category>onservability</category>
      <category>o11y</category>
      <category>prometheus</category>
    </item>
    <item>
      <title>curl, downloads, ouputs e muitos "o"s</title>
      <dc:creator>Marcelo Andrade</dc:creator>
      <pubDate>Mon, 22 Aug 2022 15:26:22 +0000</pubDate>
      <link>https://dev.to/marcelo_devsres/curl-downloads-ouputs-e-muitos-os-4cdk</link>
      <guid>https://dev.to/marcelo_devsres/curl-downloads-ouputs-e-muitos-os-4cdk</guid>
      <description>&lt;p&gt;Todo mundo usa &lt;strong&gt;curl&lt;/strong&gt; para baixar arquivos, certo? &lt;/p&gt;

&lt;p&gt;Já aconteceu isso aqui com você?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsm8jj03dxfyeexkit13m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsm8jj03dxfyeexkit13m.png" alt="Comando curl executando download de um binário"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Essa é a primeira diferença imediata entre o &lt;strong&gt;curl&lt;/strong&gt; e o &lt;strong&gt;wget&lt;/strong&gt;, e pode ser irritante se você está muito acostumado com o outro.&lt;/p&gt;

&lt;p&gt;Vamos "resolver o problema" progredindo os níveis da demanda.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nível 0: preguiçoso
&lt;/h2&gt;

&lt;p&gt;A primeira maneira para baixar arquivos é simplesmente não usar &lt;strong&gt;curl&lt;/strong&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wget https://releases.hashicorp.com/terraform/1.2.7/terraform_1.2.7_linux_amd64.zip

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;terraform_1.2.7_linux_amd64.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas aí não aprendemos nada de novo! Próximo!&lt;/p&gt;




&lt;h2&gt;
  
  
  Nível 1: Júnior
&lt;/h2&gt;

&lt;p&gt;A solução mais comum para este problema é usar -o especificando o nome do arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-so&lt;/span&gt; terraform.zip &lt;span class="se"&gt;\&lt;/span&gt;
https://releases.hashicorp.com/terraform/1.2.7/terraform_1.2.7_linux_amd64.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv03ouwyrgvi6nijahzas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv03ouwyrgvi6nijahzas.png" alt="Comando curl com a opção - o minúsculo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esse parâmetro é útil quando você acessa aquelas URLs todas truncadas que por acaso geram um binário para baixar, como imagem ou arquivo compactado.&lt;/p&gt;

&lt;p&gt;Mas no exemplo acima, do Terraform, eu não precisaria especificar o nome, basta "espelhar" o "nome remoto do arquivo":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;releases.hashicorp.com/terraform/1.2.7/&lt;strong&gt;terraform_1.2.7_linux_amd64.zip&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E, claro, existe uma opção mais conveniente para isso.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nível 2: pleno
&lt;/h2&gt;

&lt;p&gt;A opção  &lt;strong&gt;-O&lt;/strong&gt; extrai o "remote file name" da URL (i.e., sem o caminho completo). É exatamente o que procuramos para este caso.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ TD=https://releases.hashicorp.com/terraform/1.2.7
$ curl -sO $TD/terraform_1.2.7_linux_amd64.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk76br4a4f15huf84rc5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk76br4a4f15huf84rc5g.png" alt="Comando curl com a opção - O maiúsculo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Muito bom, mas não precisamos parar por aqui, certo?&lt;/p&gt;

&lt;p&gt;Ao fazer download de qualquer coisa - em especial, binários que vamos executar nos nossos preciosos sistemas operacionais -, idealmente deveríamos fazer várias verificações no download para garantir que ele é, de fato, o que queremos.&lt;/p&gt;

&lt;p&gt;A recomendação oficial aqui seria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Baixar o .zip com o Terraform;&lt;/li&gt;
&lt;li&gt;Baixar o arquivo de SHA256;&lt;/li&gt;
&lt;li&gt;Baixar o arquivo para verificar a assinatura do arquivo de checksum; &lt;/li&gt;
&lt;li&gt;Baixar a chave gpg da &lt;strong&gt;Hashicorp&lt;/strong&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmh7pt1ingwvwvyuuhg1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmh7pt1ingwvwvyuuhg1h.png" alt="Imagem do site da Hashicorp mostrando os itens descritos acima"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Putz, que trabalheira! Mas ossos do ofício.&lt;/p&gt;

&lt;p&gt;O acesso à chave da Hashicorp você pode viabilizar com o próprio GPG:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1eh10bx4qw17tum6n3q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1eh10bx4qw17tum6n3q.png" alt="ID da chave GPG da Hashicorp"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gpg --keyserver keyserver.ubuntu.com --recv-keys 72D7468F
gpg: key 34365D9472D7468F: public key "HashiCorp Security (hashicorp.com/security) &amp;lt;security@hashicorp.com&amp;gt;" imported
gpg: Total number processed: 1
gpg:               imported: 1

$ gpg --fingerprint 72D7468F
pub   rsa4096 2021-04-19 [SC] [expires: 2026-04-18]
      C874 011F 0AB4 0511 0D02  1055 3436 5D94 72D7 468F
uid           [ unknown] HashiCorp Security (hashicorp.com/security) &amp;lt;security@hashicorp.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Informações batem, aparentemente estamos ok aqui.&lt;/p&gt;

&lt;p&gt;Agora é só baixar os demais arquivos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sO&lt;/span&gt;   &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_linux_amd64.zip &lt;span class="se"&gt;\&lt;/span&gt;
             &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS      &lt;span class="se"&gt;\&lt;/span&gt;
             &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS.sig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b95bf790agttpmxfewo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b95bf790agttpmxfewo.png" alt="Curl tentando baixar múltiplos arquivs com um único - O maiúsculo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ué, o que deu errado?&lt;/p&gt;

&lt;p&gt;Bem simples... Você não leu a documentação.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If not told otherwise, curl writes the received data to stdout. It can be instructed to instead save that data into a local file, using the --output or --remote-name options. &lt;strong&gt;If curl is given multiple URLs to transfer on the command line, it similarly needs multiple options for where to save them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Múltiplas URLs são histórias diferentes, e você precisa especificar um parâmetro para cada uma delas. No exemplo em questão, precisamos de três -Os!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k22pu2sew3y8zu28ops.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k22pu2sew3y8zu28ops.png" alt="curl - O O O"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sOOO&lt;/span&gt; &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_linux_amd64.zip &lt;span class="se"&gt;\&lt;/span&gt;
             &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS      &lt;span class="se"&gt;\&lt;/span&gt;
             &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS.sig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meio tosco né. Mas é o jeito e temos que conviver.&lt;/p&gt;

&lt;p&gt;Ou não?&lt;/p&gt;




&lt;h2&gt;
  
  
  Nível 3: Sênior
&lt;/h2&gt;

&lt;p&gt;Se você é daqueles que não se satisfaz com a primeira solução que encontra e não liga de perder algum tempo procurando por algo que talvez não exista, este não é o caso: o &lt;strong&gt;curl&lt;/strong&gt; tem algo para você!&lt;/p&gt;

&lt;p&gt;Você pode habilitar o &lt;strong&gt;--remote-name-all&lt;/strong&gt; para resolver este assunto de maneira definitiva:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--remote-name-all&lt;/span&gt;            &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_linux_amd64.zip &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS      &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS.sig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fujz06y213bly5wcnu1pb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fujz06y213bly5wcnu1pb.png" alt="curl remote name all"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O Daniel Stenberg conta a história desta opção &lt;a href="https://daniel.haxx.se/blog/2020/04/27/curl-ootw-remote-name-all/" rel="noopener noreferrer"&gt;neste blog post&lt;/a&gt; bem interessante sobre o histórico de opções de saída do curl. E embora a opção esteja disponível desde &lt;strong&gt;2008&lt;/strong&gt;, segundo ele mesmo, no post de 2020, ela persiste como uma das opções mais obscuras e menos usadas do curl:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27p201mmnmqp0uowrrqn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27p201mmnmqp0uowrrqn.png" alt="Excerto do post do Daniel confirmando o parágrafo acima"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Nível 4: Especialista
&lt;/h2&gt;

&lt;p&gt;Apenas a título de completude, já que estamos falando sobre essas opções, vou deixar uma opção que não se encaixa no exemplo acima por utilidade.&lt;/p&gt;

&lt;p&gt;Neste mesmo post, temos um parágrafo:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Esse parâmetro é útil quando você acessa aquelas URLs todas truncadas que por acaso geram um binário para baixar, como imagem ou arquivo compactado.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Normalmente sites que fazem isso usam cabeçalhos especiais para indicar o nome do arquivo. &lt;/p&gt;

&lt;p&gt;Se você for na Wikipédia e tentar baixar um artigo, como, por exemplo, a seção sobre &lt;a href="https://en.wikipedia.org/w/index.php?title=Special:DownloadAsPdf&amp;amp;page=Terraforming&amp;amp;action=show-download-screen" rel="noopener noreferrer"&gt;Terraforming&lt;/a&gt;, você será apresentado a um botão.&lt;/p&gt;

&lt;p&gt;Esse botão é a seguinte URL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/api/rest_v1/page/pdf/Terraforming" rel="noopener noreferrer"&gt;https://en.wikipedia.org/api/rest_v1/page/pdf/Terraforming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você fizer uma "análise exploratória" na URL, você vai observar alguns cabeçalhos interessantes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://en.wikipedia.org/api/rest_v1/page/pdf/Terraforming | fgrep &lt;span class="nt"&gt;-i&lt;/span&gt; content-disposition
content-disposition: attachment&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Terraforming.pdf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; filename&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;UTF-8&lt;span class="s1"&gt;''&lt;/span&gt;Terraforming.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O nome do arquivo é indicado pelo cabeçalho de &lt;strong&gt;content-disposition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Se você usar o &lt;strong&gt;curl&lt;/strong&gt; com o parâmetro -J combinado com o -O, você consegue baixá-lo com o "nome certo":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sOJ&lt;/span&gt; https://en.wikipedia.org/api/rest_v1/page/pdf/Terraforming
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09o97l2wgt95xq5ja03x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09o97l2wgt95xq5ja03x.png" alt="curl - s O maiúsculo J maiúsculo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Bônus: verificação de download
&lt;/h2&gt;

&lt;p&gt;Neste post, usei o download do Terraform como motivação para avaliar os parâmetros do &lt;strong&gt;curl&lt;/strong&gt;. Falei sobre download seguro e até baixei a chave GPG da Hashicorp, mas não conclui... Vou colocar de bônus aqui no fim!&lt;/p&gt;

&lt;p&gt;Baixamos os arquivos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--remote-name-all&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_linux_amd64.zip &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS      &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nv"&gt;$TD&lt;/span&gt;/terraform_1.2.7_SHA256SUMS.sig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos baixar a chave de vários jeitos, e este é um deles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--keyserver&lt;/span&gt; keyserver.ubuntu.com &lt;span class="nt"&gt;--recv-keys&lt;/span&gt; 72D7468F
gpg: key 34365D9472D7468F: public key &lt;span class="s2"&gt;"HashiCorp Security (hashicorp.com/security) &amp;lt;security@hashicorp.com&amp;gt;"&lt;/span&gt; imported
gpg: Total number processed: 1
gpg:               imported: 1

&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--fingerprint&lt;/span&gt; 72D7468F
pub   rsa4096 2021-04-19 &lt;span class="o"&gt;[&lt;/span&gt;SC] &lt;span class="o"&gt;[&lt;/span&gt;expires: 2026-04-18]
      C874 011F 0AB4 0511 0D02  1055 3436 5D94 72D7 468F
uid           &lt;span class="o"&gt;[&lt;/span&gt; unknown] HashiCorp Security &lt;span class="o"&gt;(&lt;/span&gt;hashicorp.com/security&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;security@hashicorp.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirmo a identidade com o código do site&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--lsign-key&lt;/span&gt; &lt;span class="s1"&gt;'C874 011F 0AB4 0511 0D02  1055 3436 5D94 72D7 468F'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirmo as assinaturas do arquivo .sig do Terraform com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--verify&lt;/span&gt; terraform_1.2.7_SHA256SUMS.sig
gpg: assuming signed data &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'terraform_1.2.7_SHA256SUMS'&lt;/span&gt;
gpg: Signature made Wed 10 Aug 2022 02:51:41 PM &lt;span class="nt"&gt;-03&lt;/span&gt;
gpg:                using RSA key 374EC75B485913604A831CC7C820C6D5CD27AB87
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   1  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
gpg: next trustdb check due at 2024-04-06
gpg: Good signature from &lt;span class="s2"&gt;"HashiCorp Security (hashicorp.com/security) &amp;lt;security@hashicorp.com&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;full]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arquivo de checksums está ok! E agora o download:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sha256sum --ignore-missing -c terraform_1.2.7_SHA256SUMS
terraform_1.2.7_linux_amd64.zip: OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto, pode usar o Terraform em paz!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
