Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

android-chroot-server

Transforma um celular Android rooteado num servidor Linux de verdade: Debian com systemd, subindo sozinho no boot, independente de qualquer aplicativo.

Não é proot, não é Termux, não é emulação. É um chroot real sobre uma imagem ext4, dentro de namespaces de mount e PID, com o systemd como PID 1 — iniciado pelo Magisk antes de qualquer aplicativo carregar.

Validado em: Samsung Galaxy S20+ (SM-G985F), LineageOS 23.2, Android 16. A arquitetura não tem nada de específico desse aparelho: funciona em qualquer Android com Magisk.


Índice


Por que não Termux

Os scripts populares de "Linux no Termux" têm três amarras:

Emulação. O proot intercepta e emula chamadas de sistema. Funciona, mas é lento e nem tudo se comporta como deveria.

Ciclo de vida. Tudo roda como processo filho do aplicativo. Fechou o Termux, o Android matou o processo, acabou o servidor.

Localização do rootfs. Fica em /data/data/com.termux/..., que pertence ao UID do aplicativo, é credential-encrypted (inacessível no boot antes do primeiro desbloqueio) e some se você limpar os dados do app.

Aqui os processos são nativos, iniciados pelo init do sistema via Magisk, com o rootfs em armazenamento device-encrypted. Nada disso depende de app.


Arquitetura

Android (init do sistema)
  └─ Magisk service.d → linux-start.sh
      └─ imagem ext4 montada com suid,dev
          └─ unshare -m -p  (namespaces de mount e PID)
              └─ chroot
                  └─ systemd (PID 1 lá dentro)
                      ├─ ssh.service
                      ├─ cron.service
                      └─ o que mais você quiser

Visto do Android, é um processo comum. Visto de dentro, é um sistema Linux completo com PID 1 próprio.

Decisões de projeto e o porquê

Imagem ext4, não pasta. O /data do Android é montado nosuid,nodev. Com o rootfs numa pasta, sudo e ping quebram sem mensagem clara. A imagem é montada com suid,dev e resolve. De quebra, backup vira copiar um arquivo.

Imagem esparsa. 32 GB declarados ocupam só o que for usado de fato.

unshare -m -p antes do chroot. O namespace de PID é o que permite ao systemd ser PID 1. O de mount faz todos os binds desaparecerem sozinhos quando o container morre — nada vaza para o Android.

oom_score_adj = -1000. Sem isso o lmkd do Android mata o servidor quando a memória aperta.

Wakelock de kernel. echo linux_server > /sys/power/wake_lock impede a CPU de suspender. Sem isso o servidor some da rede em segundos de inatividade.

Regra de SELinux para o driver loop. Quem lê o arquivo de imagem é uma thread do kernel no domínio u:r:kernel:s0, que não tem acesso aos rótulos de /data. Sem uma regra pontual via magiskpolicy, o mount falha com Invalid argument e o dmesg mostra avc: denied. Não é preciso setenforce 0. A regra é volátil, por isso o linux-start.sh a reaplica a cada boot.

Verificação de identidade do processo. O init.pid sobrevive ao reboot e o Android reaproveita PIDs baixos rapidamente. Checar só kill -0 já fez o script concluir que o container estava vivo quando o PID gravado havia virado com.android.packageinstaller. Agora os scripts confirmam que a raiz do processo é a nossa imagem.


Instalação

Pré-requisitos

  • Android com Magisk (root) e depuração USB ativada
  • platform-tools (adb) no computador
  • ~2 GB livres no /data para começar

0. Baixar um rootfs arm64

Debian 13 (trixie) — usado na validação:

https://raw.githubusercontent.com/debuerreotype/docker-debian-artifacts/dist-arm64v8/trixie/oci/blobs/rootfs.tar.gz

Ubuntu Base 24.04 — alternativa: https://cdimage.ubuntu.com/ubuntu-base/releases/24.04/release/

Deixe o arquivo na pasta host/.

1. Enviar os scripts

No Windows, dentro de host/:

.\deploy.bat

O .bat chama o PowerShell com -ExecutionPolicy Bypass só nessa execução, sem alterar a configuração do sistema. Ele localiza o adb, confere o dispositivo e envia tudo — inclusive o rootfs, se estiver na pasta.

Em Linux ou macOS, basta adb push dos arquivos de device/ para /sdcard/Download/.

2. Abrir o shell do aparelho

adb shell
su
sh /sdcard/Download/prepare.sh

O Magisk vai pedir permissão na tela do celular. Se a tela estiver bloqueada, o comando fica pendurado. Desbloqueie e toque em Permitir.

O prepare.sh instala os scripts em /data/linux/, corrige quebra de linha CRLF (que causa erros not found incompreensíveis) e imprime os próximos passos.

Todo o resto acontece dentro do aparelho. Comandos longos com aspas passados via adb shell são reinterpretados pelo shell do Android e quebram de formas difíceis de depurar.

3. Faxina (se houver instalação anterior)

Primeiro em simulação — não altera nada:

/data/local/tmp/cleanup-old.sh

Ele varre /data atrás de rootfs antigos (até 14 níveis de profundidade — o chroot-distro do Termux enterra o dele fundo), lista caminho, tamanho e distro de cada um, e procura processos rodando dentro deles. Um chroot é só um processo do Android com outra raiz: apagar os arquivos não mata o processo, e enquanto ele viver vai segurar portas.

Conferindo que a lista está correta:

/data/local/tmp/cleanup-old.sh --apply

4. Criar a imagem e instalar o rootfs

/data/linux/install-rootfs.sh /sdcard/Download/rootfs.tar.gz

Padrão de 32 GB. Para mudar: SIZE_GB=64 /data/linux/install-rootfs.sh ...

5. Configurar o Debian

/data/linux/run-setup.sh

Instala systemd, SSH, sudo, cron e utilitários; mascara as unidades que não fazem sentido num container (getty, udevd, modules-load); ajusta fuso e locale; cria o usuário server; e configura o journal como volátil, para não desgastar a memória flash do aparelho à toa.

Não define senhas. Isso fica por sua conta, no passo seguinte.

6. Definir as senhas e testar

/data/linux/linux-start.sh
/data/linux/linux-shell.sh

Dentro do Debian:

passwd server
exit

De outra máquina da rede:

ssh server@<ip-do-celular>

O linux-status.sh mostra IP, portas ouvindo, estado do wakelock e da bateria.

7. Autostart no boot

Só depois que o SSH funcionar:

cp /data/linux/99-linux.sh /data/adb/service.d/99-linux.sh
chmod 755 /data/adb/service.d/99-linux.sh

Reinicie sem abrir o Termux e confirme que o SSH responde. Esse é o teste que prova a independência.

O 99-linux.sh também deixa um watchdog: se o container cair, reergue em até 60 s. Uma parada intencional pelo linux-stop.sh é respeitada — ele cria uma marca que o watchdog verifica.


Operação diária

Ação Comando (root no Android)
Diagnóstico completo /data/linux/linux-status.sh
Iniciar /data/linux/linux-start.sh
Parar /data/linux/linux-stop.sh
Shell /data/linux/linux-shell.sh
Log de boot cat /data/linux/boot.log
Backup parar e copiar /data/linux/rootfs.img

O linux-shell.sh detecta se o container está vivo: se estiver, entra nele por nsenter; se não, abre um chroot temporário de setup.

Para execução não interativa:

SHELL_CMD='/bin/bash /root/meu-script.sh' /data/linux/linux-shell.sh

Docker (opcional)

Requer um kernel recompilado. O kernel que as ROMs distribuem vem com IPC_NS, POSIX_MQUEUE, CGROUP_DEVICE e BRIDGE desativados, e sem eles o dockerd não sobe — não há flag que contorne.

Para descobrir se o seu precisa:

adb push docker/docker-check.sh /data/local/tmp/
adb shell su -c 'sh /data/local/tmp/docker-check.sh'

Para Exynos 990 (Galaxy S20 / Note 20), a receita de recompilação está em exynos990-docker-kernel. Para outros aparelhos o princípio é o mesmo, mudando a árvore do kernel.

Com o kernel pronto, docker/DOCKER.md cobre instalação, uso e limitações. Resumo: funciona, com --network=host no lugar de -p porta:porta.


Ajustes para uso 24/7

Bateria — o item mais importante

Celular plugado em 100% o dia inteiro incha a bateria em poucos meses. Veja Configurações > Bateria > Controle de carregamento do LineageOS. Se não existir, use o módulo Magisk ACC (Advanced Charging Controller):

acc -s capacity 40 60 60

Rede

adb shell settings put global wifi_sleep_policy 2
adb shell settings put global adaptive_battery_management_enabled 0

E reserve um IP fixo no DHCP do roteador.

Para rede cabeada, um hub USB-C com PD passthrough permite carregar e usar Ethernet ao mesmo tempo — muda o patamar de estabilidade.

Térmica

Sem ventoinha o aparelho throttla. Tire a capa. Se necessário, prenda os processos pesados nos núcleos pequenos com taskset -c 0-3.

Armazenamento

/mnt/android dentro do Linux aponta para /data/media/0 — acesso direto ao armazenamento interno, sem passar pelo FUSE. Duas ressalvas: é credential-encrypted, então só fica legível depois do primeiro desbloqueio após um reboot; e arquivos criados por ali não aparecem na galeria até um rescan. Para dados de serviço, use a própria imagem ext4 ou um HD USB em ext4.


Solução de problemas

not found ao rodar qualquer script Quebra de linha CRLF. sed -i 's/\r$//' /data/linux/*.sh

mount ... failed: Invalid argument SELinux bloqueando o driver loop. Confirme no dmesg: avc: denied { read } ... scontext=u:r:kernel:s0. O fix-sepolicy.sh resolve com uma regra pontual via magiskpolicy, reaplicada a cada boot pelo linux-start.sh.

Serviço sobe mas não responde na rede Falta o grupo aid_inet (GID 3003). O kernel do Android tem paranoid network: sem esse grupo suplementar, um usuário não-root não abre socket — e falha sem mensagem. usermod -aG aid_inet <usuario>

sudo: effective uid is not 0 A imagem montou nosuid. Confirme com linux-status.sh.

systemd em degraded Quase sempre getty tentando abrir um console inexistente. systemctl --failed mostra. O setup-debian.sh já mascara.

Address already in use na porta 22 Processo de uma instalação anterior ainda vivo. Descubra com readlink /proc/<pid>/root e mate. O cleanup-old.sh faz isso.

Container não sobe após reboot Pidfile obsoleto com PID reaproveitado. Os scripts atuais verificam a identidade do processo pela raiz; se estiver com versão antiga, atualize.


Estrutura

host/       executados no computador
  deploy.ps1 / deploy.bat   envia tudo por adb
  prepare.sh                instala os scripts no aparelho

device/     executados no aparelho, como root
  install-rootfs.sh    cria a imagem ext4 e extrai o rootfs
  migrate-rootfs.sh    migra um chroot existente para a imagem
  cleanup-old.sh       remove instalações anteriores (simulação por padrão)
  run-setup.sh         dispara o setup-debian.sh dentro do chroot
  setup-debian.sh      configura o Debian (roda dentro do chroot)
  linux-start.sh       sobe o container
  linux-stop.sh        desliga e desmonta
  linux-shell.sh       shell dentro do container
  linux-status.sh      diagnóstico
  fix-sepolicy.sh      regra de SELinux para o driver loop
  99-linux.sh          autostart + watchdog (vai em /data/adb/service.d/)

docker/     Docker dentro do container (requer kernel recompilado)
diag/       scripts de diagnóstico pontual

About

Servidor Linux com systemd em Android rooteado, via chroot em imagem ext4 iniciado pelo Magisk

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages