GCP OSLogin: entendendo como funciona!
Published: 2023-12-14 05:29:04

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?

Então vamos tirar isso à prova fazendo um estudo de caso do recurso **OSLogin** do **Google Cloud**. No final, me conte se aprendeu algo diferente ou não!

## OS Login

De acordo com a [documentação](https://cloud.google.com/compute/docs/oslogin), **OS Login** é um recurso para melhor gerir o acesso via SSH às suas máquinas virtuais Linux no GCP. Ele faz mais que gerenciar chaves:

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

Eu vejo muitas comparações com o [**AWS Systems Manager - Session Manager**](https://docs.aws.amazon.com/pt_br/systems-manager/latest/userguide/session-manager.html), mas é um recurso muito mais parecido com outro recurso da AWS chamado [**Instance Connect**](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-eic.html).

## Como acessar VMs

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:

```bash
$ 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:~$
```

Se sua máquina **não** tiver um endereço IP público para acesso direto, o **Gcloud** é esperto o suficiente para saber que [precisa de um passo a mais para viabilizar](https://cloud.google.com/iap/docs/using-tcp-forwarding):

```bash
$ gcloud compute ssh instance-2
External IP address was not found; defaulting to using IAP tunneling.
...
```

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

## Como funciona o OS Login - cliente SSH

Ao tentar usar OS Login a primeira vez, você deverá receber a seguinte mensagem:

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

Ainda que você tenha chaves - mesmo com os nomes padrão id_rsa, id_ecdsa, id_ed25519 -, ele irá gerar uma nova para você:

```bash
$ ls -la ~/.ssh/google*
-rw------- 1 marcelo marcelo 2602 Dec 13 23:38 /home/marcelo/.ssh/google_compute_engine
-rw-r--r-- 1 marcelo marcelo  571 Dec 13 23:38 /home/marcelo/.ssh/google_compute_engine.pub
-rw-r--r-- 1 marcelo marcelo  218 Dec 13 23:44 /home/marcelo/.ssh/google_compute_known_hosts
```

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


A chave gerada é uma RSA de 3072 bits:

```bash
# O comando executado deve ter sido:
# ssh-keygen -t rsa -b 3072 ~/.ssh/google_compute_engineq
$ ssh-keygen -l -f ~/.ssh/google_compute_engine.pub
3072 SHA256:W0xABfw0HgNyBgsPX6av+co9ZM/JWaa893fcIhM3zL8 marcelo@dominator (RSA)
```

Após gerar a chave, o gcloud "sobe" a chave pública para o GCP sem você ver:

```bash
$ gcloud compute os-login ssh-keys list
FINGERPRINT                                                       EXPIRY
eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
```

É possível ver informações sobre a chave executando o comando:

```bash
$ gcloud compute os-login ssh-keys describe --key eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
fingerprint: eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
key: ssh-rsa ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCsPAqzEGyyz5ltlhJd1wxSyde2WOkDz4baJkDyB90dXstaVLkrw64LfYTIu0W2gpJOLkzOsUNjhTJl/3Feda+GvTIpNpom7EboCYbNd9ExTWnw2W1d6Y/Inn1vydOHoi1qt7X97QOfg7avDkYHoXhHMxumRetUWxte1FBKZ8se3KAPUH/GCBEB28EO6OOqs89c9MmjxWq9d1l0iMioGzGRkM2DvowhmVe5NwrYZMeSXBd7x0bGkDTpdaT5RFJf1nvBy8a68SQbTGO6d9KsqsGmTyUrUPhylkG+yzG+DW4p+KRsC8M6bXX0J3DCiHA9evLT6MyuTpOJDpxJAb+UAdXNwNn6T154HJZ1s1w4u5ruenAepaXBqI8EzGKBy0VUBObupQZuEMNj7XkKTlF3hgjwDvnMQr0AbDsz7da/uMhcVjoUCGZPRmeYRtCpUaEG453W9ZgNlQYALy18D4NEaJ85XiQQUQWDbBoEUcdl6R0JCIJK6pe4HxTyJT05I/QYNus=
  marcelo@devsres.com
name: users/cloud_user_p_0fffe37e@linuxacademygclabs.com/sshPublicKeys/eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
```

É possível reusar suas chaves sem dificuldade:

```bash
$ gcloud compute os-login ssh-keys add --key-file ~/.ssh/id_rsa.pub --ttl 30
...
$ gcloud compute os-login ssh-keys list
FINGERPRINT                                                       EXPIRY
ca30c038ede7cc7ed7bee957d7778988b540f8204beabe2cdccf00ae78031a30  2023-12-14T03:19:19Z
eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
...
```

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).

## Como funciona o OS Login no servidor

### No Linux: NSS

Agora sim, a parte interessante!

Primeiramente, vamos observar com que usuário você autentica no servidor remoto:

```bash
$ gcloud compute ssh instancia-publica --project=playground-s-11-b0bd65c9 --zone=us-central1-a
...
cloud_user_p_0fffe37e_linuxacade@instance-1:~$ whoami
cloud_user_p_0fffe37e_linuxacade
```

O usuário está associado ao meu usuário do Google Cloud - que é meio grande e não coube: **cloud_user_p_0fffe37e@linuxacademygclabs.com**.

Aqui a primeira diferença deste recurso para o SSM Session Manager e o Instance Connect: esse usuário **não foi criado localmente na máquina**: 

```bash
[instancia-publica] $ grep cloud_user /etc/passwd | echo nao achei nada
nao achei nada
```

Isso acontece porque o OS Login integra com uma velha conhecida no mundo Linux, a **libnss**, por meio de um módulo específico (na verdade, dois):

```bash
[instancia-publica] $ head -n 13 /etc/nsswitch.conf
# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.

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

hosts:          files dns
networks:       files
```

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

Essas libs são instaladas pelo pacote:
```bash
[instancia-publica] $ dpkg -L 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
```

Também há **units** do systemd ativas com estes programas:
```bash
[instancia-publica] $ systemctl list-units 'google*'
  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
```

Dependendo do provider, é possível ver as entradas usando o comando abaixo:

```bash
[instancia-publica] $ getent passwd | tail -n5
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:*:1862762045:1862762045::/home/cloud_user_p_0fffe37e_linuxacade:/bin/bash
sa_112203697991872690750:*:3661484281:3661484281::/home/sa_112203697991872690750:/bin/bash
sa_109344113766727672345:*:3686152525:3686152525::/home/sa_109344113766727672345:/bin/bash
```

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.

Se seu usuário não fosse visível para o SO, você não conseguiria nem logar!

Se ele não surgir na saída do comando acima, é possível fazer uma query direta sobre ele especificando o nome:

```bash
[instancia-publica] $ id
uid=1862762045(cloud_user_p_0fffe37e_linuxacade) gid=1862762045(cloud_user_p_0fffe37e_linuxacade) groups=1862762045(cloud_user_p_0fffe37e_linuxacade),44(video)

[instancia-publica] $ getent passwd cloud_user_p_0fffe37e_linuxacade
cloud_user_p_0fffe37e_linuxacade:*:1862762045:1862762045::/home/cloud_user_p_0fffe37e_linuxacade:/bin/bash
```

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!

Agora, uma outra consideração: as conexões realmente ocorrem via **ssh** a partir do endereço da sua máquina!

```bash
[instancia-publica] $ ss -nt '( sport = :22 )'
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
```

No caso, o IP **XXX.42.69.YYY** representa o meu endereço IP. 

Se você executar o comando a partir de um tunel IAP, o endereço vai ser outro:

```bash
[instancia-publica] $ ss -nt '( sport = :22 )'
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
```

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 [35.235.240.0/20](https://cloud.google.com/iap/docs/using-tcp-forwarding#:~:text=allows%20ingress%20traffic%20from%20the,and%20port%203389%20for%20RDP.), como descrito na documentação).

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

### No Linux: PAM

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

```bash
[instancia-publica] $ ls /etc/pam.d
chfn      chsh            common-auth      common-session                 cron   newusers  passwd   runuser-l  su    sudo
chpasswd  common-account  common-password  common-session-noninteractive  login  other     runuser  sshd       su-l
```

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.

Esta é a configuração PAM do comando **sudo**:

```bash
[instancia-publica] $ egrep -v '^$|^#'  /etc/pam.d/sudo
@include common-auth
@include common-account
@include common-session-noninteractive
```

Esta, a do **su**:
```bash
[instancia-publica] $ egrep -v '^$|^#'  /etc/pam.d/su
auth          sufficient pam_rootok.so
session       required   pam_env.so readenv=1
session       required   pam_env.so readenv=1 envfile=/etc/default/locale
session       optional   pam_mail.so nopen
session       required   pam_limits.so
@include common-auth
@include common-account
@include common-session
```

E esta a do **ssh** (**se** a diretiva **UsePAM** estiver configurada!):
```bash
[instancia-publica] $ sudo fgrep -i 'UsePAM'  /etc/ssh/sshd_config
UsePAM yes

[instancia-publica] $ egrep -v '^$|^#'  /etc/pam.d/sshd
auth       [default=ignore] pam_group.so
@include common-auth
account    required     pam_nologin.so
@include common-account
session [success=ok ignore=ignore module_unknown=ignore default=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  motd=/run/motd.dynamic
session    optional     pam_motd.so noupdate
session    optional     pam_mail.so standard noenv # [1]
session    required     pam_limits.so
session    required     pam_env.so # [1]
session    required     pam_env.so user_readenv=1 envfile=/etc/default/locale
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so open
@include common-password
session    [success=ok default=ignore] pam_mkhomedir.so
```

Achou complicado? Note que em todos há **includes** 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!

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

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

```bash
[instancia-publica] $ cat /etc/sudoers.d/google-oslogin
#includedir /var/google-sudoers.d

[instancia-publica] $ cat /var/google-sudoers.d/*
cloud_user_p_0fffe37e_linuxacade ALL=(ALL) NOPASSWD: ALL
```

### No SSH

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

```bash
[instancia-publica] $ head -n7 /etc/ssh/sshd_config
#### Google OS Login control. Do not edit this section. ####
TrustedUserCAKeys /etc/ssh/oslogin_trustedca.pub
AuthorizedPrincipalsCommand /usr/bin/google_authorized_principals %u %k
AuthorizedPrincipalsCommandUser root
AuthorizedKeysCommand /usr/bin/google_authorized_keys
AuthorizedKeysCommandUser root
#### End Google OS Login control section. ####
```

As três primeiras diretivas estão relacionadas ao uso de [**certificados SSH**](https://goteleport.com/blog/how-to-ssh-properly/), 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!

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

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!

```bash
[instancia-publica] $ /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= marcelo@devsres.com
```

Caso mais uma chave seja adicionada do lado do cliente:

```bash
$ gcloud compute os-login ssh-keys add --key-file ~/.ssh/id_ecdsa.pub --ttl 30
...

$ gcloud compute os-login ssh-keys list
FINGERPRINT                                                       EXPIRY
68dc496cf5293b7fa094a05b0f0acc92d2382e7bb983a70bacae1ea06cbfaeed  2023-12-14T05:27:22Z
eee442d8830969ce8109c1c62a8d1aed1c188c11e87c7beebdb8610269724138
```

A saída do outro comando irá mostrar todas as chaves disponíveis!

```bash
[instancia-publica] $ /usr/bin/google_authorized_keys cloud_user_p_0fffe37e_linuxacade
fiXhlbf7uTxcmcYKSrFRakfPRyVtpUVaBLoTJHh70OvITNBXyCiUxwBGM6kKwqSWGX6uD3Bk8nal6cG4eZFHGFy8mfCK3Cx7wNShvXfXEVOqALXJI5W8NPl3eIOMQhx+S1mrK+45K4qTqfaUuq9W8MdrzUEfB4y/DbsgcY4p6GAqTBURuMU0Ym3d+H7DAo4rJCTpy1d/qTvCTkN14Y3slIshmzORmuo2caPpPMsX7iJtpLZkmMJ872gTKU3P5sx7efEv1D89i1WMHFpWAqhIRsKNsJCB53r0Dtt6dK2iKeonWgZYMYfKd+3Px8eDgYyOetNVFhaGXv9w/bDDj60pchENCEDTB59CdAU71tiI/U7738DITq1ZZVzCSWpu0= cloud_user_p_0fffe37e@cs-380498931324-default
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCsPAqzEGyyz5ltlhJd1wxSyde2WOkDz4baJkDyB90dXstaVLkrw64LfYTIu0W2gpJOLkzOsUNjhTJl/3Feda+GvTIpNpom7EboCYbNd9ExTWnw2W1d6Y/Inn1vydOHoi1qt7X97QOfg7avDkYHoXhHMxumRetUWxte1FBKZ8se3KAPUH/GCBEB28EO6OOqs89c9MmjxWq9d1l0iMioGzGRkM2DvowhmVe5NwrYZMeSXBd7x0bGkDTpdaT5RFJf1nvBy8a68SQbTGO6d9KsqsGmTyUrUPhylkG+yzG+DW4p+KRsC8M6bXX0J3DCiHA9evLT6MyuTpOJDpxJAb+UAdXNwNn6T154HJZ1s1w4u5ruenAepaXBqI8EzGKBy0VUBObupQZuEMNj7XkKTlF3hgjwDvnMQr0AbDsz7da/uMhcVjoUCGZPRmeYRtCpUaEG453W9ZgNlQYALy18D4NEaJ85XiQQUQWDbBoEUcdl6R0JCIJK6pe4HxTyJT05I/QYNus= marcelo@devsres.com
```

É ou não é um recurso interessante?

Isso porque eu ainda não mostrei a funcionalidade de [usar SSH com MFA](https://cloud.google.com/compute/docs/oslogin/set-up-oslogin) também disponível!