Hospedando Forgejo com Sandhole
Por Eric Rodrigues Pires • 10 minutos de leitura •
Às vezes, você só quer hospedar os serviços que você usa. Talvez você esteja preocupado com a segurança dos seus dados, ou você discorda das ações ou Termos de Uso de um dos seus serviços atuais, ou você tem um drive de 1 Terabyte em desuso que seria muito caro para usar na nuvem... ou talvez um novo repositório open-source chamou sua atenção, e você quer experimentar com ele.
Graças a Docker/Podman, NixOS, ou até mesmo K3s, subir um serviço em um servidor caseiro é tranquilo – mas e se você gostaria que ele estivesse disponível para além da rede local? Daí você precisaria de um IP público, que seu provedor de Internet quase certamente não providenciará, e o IPv6 ainda não é tão universal quanto deveria ser.
Felizmente, você consegue comprar uma VPS barata com um endereço IP público com extrema facilidade nos dias de hoje (de provedores cloud como Hetzner ou DigitalOcean, ou Magalu Cloud se você estiver no Brasil como eu), mas você estará sujeito a armazenar seus dados externamente e limitado de recursos computacionais. Se você quiser combinar o melhor de dois mundos – um IP público com controle total sobre o seu serviço –, então o Sandhole pode ser exatamente o que você precisa.

Assim como ngrok ou Tailscale Funnel, Sandhole é um proxy reverso que expõe servidores atrás de CGNAT à Internet – conseguindo essa proeza através de funcionalidades do OpenSSH, como redirecionamento remoto (remote forwarding). A autenticação é tão simples quanto utilizar chaves SSH e o redirecionamento de portas funciona independentemente da topologia da sua rede; tudo que você precisa para usar uma instância do Sandhole é conseguir alcançá-la pela rede!
Diferentemente destas ferramentas, Sandhole é completamente open-source, sendo que auto-hospedagem é o seu principal caso de uso. Escrito em Rust, ele é distribuído como um único arquivo binário que roda em qualquer lugar (até mesmo no Windows!), e pode facilmente lidar com várias conexões simultâneas sem consumir muita CPU ou RAM.
Neste guia, vamos configurar nossa pŕopria forja de código (Git forge), criar nossa instância do Sandhole para expô-la ao mundo, e ver quais desafios podemos encontrar no caminho.
Requisitos
Você precisará dos seguintes itens para seguir este guia (edite os campos com os seus dados):
Um endereço de e-mail para gerar certificados do Let's Encrypt (indicado como ).
Um servidor com um endereço IPv4 público para rodar o Sandhole (indicado como ). Opcionalmente – ou alternativamente –, você também pode utilizar um endereço IPv6 (indicado como ).
Um servidor doméstico conectado à Internet.
Um nome de domínio, mais seus subdomínios, apontando para o IP público em (2) (indicado como ).
Embora (1) e (2) sejam obrigatórios, você pode até se virar sem os outros dois itens. Por exemplo, (3) poderia muito bem ser o mesmo servidor que (2). Mas não usar (4) é complicado – ele requer que você use um serviço como sslip.io, o que involve muitos detalhes técnicos que estão fora do escopo deste guia simplificado.
Porém, (2) e (4) não precisam ser de um servidor que seja seu! Ele pode pertencer a uma instância do Sandhole administrada por outra pessoa, como uma amiga de confiança.
Para fins de demonstração, já que o nosso domínio será sandhole.example, vamos rodar nosso servidor auto-hospedado no subdomínio git.sandhole.example.
A configuração mínima do nosso DNS será algo como:
| Tipo | Registro | Valor |
|---|---|---|
| A | sandhole.example | <ENDEREÇO IPv4> |
| A | *.sandhole.example | <ENDEREÇO IPv4> |
| AAAA | sandhole.example | <ENDEREÇO IPv6> |
| AAAA | *.sandhole.example | <ENDEREÇO IPv6> |
| NS | _acme-challenge.sandhole.example | sandhole.example |
Forgejo
Nós iremos configurar uma instância do Forgejo, com uma interface web e porta SSH (para tornar os pulls/pushes ergonômicos).
Eu vou usar o NixOS como exemplo, já que é minha obsessão atual, mas também por questão de brevidade. Outros métodos de instalação funcionarão da mesma maneira.
{ ... }:
{
# ...
services.forgejo = {
enable = true;
database.type = "postgres";
settings = {
server = {
HTTP_ADDR = "::";
HTTP_PORT = 3000;
ROOT_URL = "https://git.sandhole.example:443/";
LOCAL_ROOT_URL = "https://git.sandhole.example:443/";
SSH_PORT = 22;
SSH_LISTEN_PORT = 2222;
START_SSH_SERVER = true;
SSH_DOMAIN = "git.sandhole.example";
};
service = {
DISABLE_REGISTRATION = true;
REQUIRE_SIGNIN_VIEW = false;
};
repository = {
DISABLE_STARS = true;
MAX_CREATION_LIMIT = 0;
};
};
};
networking.firewall.allowedTCPPorts = [
3000
2222
];
}Aqui, nós usamos o nosso subdomínio especial, mas ainda podemos acessar a instância do Forgejo em localhost:3000. Você pode temporariamente definir serives.forgejo.settings.service.DISABLE_REGISTRATION como false para criar a conta de administrador, mas essa será toda a configuração do Forgejo que faremos no momento.
Note que estamos definindo portas de produção para os endpoints voltados a usuários: 443 para HTTPS e 22 para SSH. É aqui que o Sandhole entra para nos ajudar – mesmo que ninguém mais possa acessar nosso servidor no momento.
Sandhole
Como mencionei anteriormente, vamos configurar o Sandhole numa VPS com IP público. Há múltiplas maneiras de configurá-lo, mas vamos seguir com Docker Compose para fins de simplicidade.
Após alugar um novo servidor, algo que você talvez queira fazer é trocar a porta do servidor OpenSSH para 2222, de tal forma que os usuários do Forgejo consigam se conectar pela porta 22 (que será gerenciada pelo Sandhole). Se você quiser fazer isso, então:
- Habilite conexões de entrada pela porta TCP 2222 no firewall (se você tiver um);
- Adicione a linha
Port 2222ao arquivo/etc/ssh/sshd_config; - Reinicie o daemon do SSH; e
- Certifique-se de que você consegue se conectar ao servidor na nova porta de outro terminal – caso contrário, você perderá o acesso à VPS ao se deslogar.
Agora, vamos subir nossa instância do Sandhole. Primeiro, iremos configurar o Agnos – um serviço que irá gerar os certificados wildcard para nosso domínio e subdomínios. Você lembra quando criamos um registro NS apontando para o próprio servidor mais cedo? O Agnos é o motivo! Enfim, ele espera um arquivo de configuração config.toml:
dns_listen_addr = "[::]:53"
[[accounts]]
email = "admin@your.email"
private_key_path = "agnos/letsencrypt_key.pem"
[[accounts.certificates]]
domains = ["sandhole.example", "*.sandhole.example"]
fullchain_output_file = "agnos/sandhole.example/fullchain.pem"
key_output_file = "agnos/sandhole.example/privkey.pem"
reuse_private_key = trueE aqui está nossa configuração do Docker Compose:
container_name: sandhole_agnos
restart: unless-stopped
ports:
- "53:53/udp"
volumes:
- ./agnos:/agnos:rw
- ./config.toml:/config.toml:ro
command:
- sh
- -c
- >
agnos-generate-accounts-keys --key-size 4096
--no-confirm config.toml
&& agnos --no-staging config.toml
&& echo 'Retrying in one hour...'
&& sleep 3600
sandhole:
image: docker.io/epiceric/sandhole:latest
container_name: sandhole
restart: unless-stopped
volumes:
- ./deploy:/deploy:rw
- ./agnos:/agnos:ro
network_mode: host
command: |
--domain=sandhole.example
--acme-contact-email=admin@your.email
--user-keys-directory=/deploy/user_keys/
--admin-keys-directory=/deploy/admin_keys/
--certificates-directory=/agnos/
--acme-cache-directory=/deploy/acme_cache/
--private-key-file=/deploy/server_keys/ssh
--ssh-port=22
--http-port=80
--https-port=443
--allow-requested-subdomains
--force-https
--connect-ssh-on-https-portFinalmente, temos o Sandhole rodando em um servidor! Se você abriu todas as portas necesssárias no firewall, incluindo a porta 53/udp do Agnos, ele deveria funcionar sem mais ajustes. Mesmo assim, o Sandhole não é útil até que conectemos nosso servidor nele, então vamos fazer isso agora.
Proxy reverso
O propósito do Sandhole é facilitar o processo de expor serviços à Internet de forma segura. Nós vamos precisar de uma chave SSH, já que é isso que o Sandhole usa para autenticação, e vamos dar um nome e comentário a ela que identifique o seu propósito.
ssh-keygen -t ed25519 -f ~/.ssh/git.sandhole.example -C "git.sandhole.example"Isto gerará dois arquivos em ~/ssh, mas só estamos interessados em git.sandhole.example.pub (a chave pública) no momento.
De volta ao servidor do Sandhole, na raiz do nosso Docker Compose, adicione uma cópia da chave git.sandhole.example.pub a ./deploy/user_keys/. Quaisquer chaves neste diretório (que foi definido por --user-keys-directory) são autorizadas a fazer proxy de serviços.
Vamos usar autossh no servidor doméstico rodando Forgejo, para manter uma conexão persistente dele ao Sandhole. Para o nosso exemplo do NixOS, isso é tão simples quanto adicionar uma configuração nova em services.autossh.sessions (com o caminho correto para nossa chave SSH privada):
{ ... }:
{
# ...
services.autossh.sessions = [
{
name = "forgejo";
user = "root";
extraArguments = ''
-i /home/eu/.ssh/git.sandhole.example \
-o StrictHostKeyChecking=accept-new \
-o ServerAliveInterval=30 \
-R git.sandhole.example:80:localhost:3000 \
-R git.sandhole.example:22:localhost:2222 \
sandhole.example
'';
}
];
}Como alternativa, você pode usar o Docker Compose com a imagem epiceric/sandhole-client, tal como este exemplo.
Se a conexão falhar por conexões de saída na porta SSH serem barradas, tente adicionar -p 443 aos argumentos de linha de comando do autossh. Já que configuramos --connect-ssh-on-https-port no Sandhole mais cedo, nós podemos conectar com SSH à porta HTTPS do nosso servidor!
Agora, quando você acessar https://git.sandhole.example, sua instância do Forgejo deverá aparecer magicamente, com um certificado TLS e tudo o mais!
ProxyJump
Tem apenas mais um detalhe com o qual precisamos nos preocupar.
Se você tentar clonar ou fazer push de um repo via SSH, você irá receber um erro críptico:
exec request failed on channel 0
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.A razão é que, embora o host SSH git.sandhole.example aponte para o Sandhole, não tem nada que diga que ele use o nosso serviço SSH atrás da proxy – em outras palavras, o Sandhole está sendo considerado como nosso servidor SSH do Forgejo! Enquanto que o protocolo HTTP tem um cabeçalho Host para fazer multiplexing de vários servidores HTTP ao mesmo tempo, não tem nada parecido com isso no SSH; então nós iremos manualmente especificar para qual "host virtual" nós queremos "pular", com a seguinte configuração no arquivo ~/.ssh/config:
Host git.sandhole.example
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
ProxyJump sandhole.exampleA parte importante é o ProxyJump. Ele diz para o SSH fazer proxy da conexão a git.sandhole.example através do servidor SSH real, sandhole.example. Normalmente, você usaria isso para alcançar um host real dentro de uma VPN, mas no nosso caso, o Sandhole usa magia de aliasing para redirecionar conexões ao serviço apropriado nos hosts virtuais do seu proxy.
Você precisará instruir quaisquer usuários da sua instância do Forgejo a fazer o mesmo, mas depois de configurar o ProxyJump, você pode esquecer completamente que ele existe.
Próximos passos
E isso é tudo! Nossa instância do Forgejo está completamente operacional e exposta via Sandhole. Claro, isso é só o começo, e você pode mexer com as opções do Sandhole ou expor ainda mais serviços TCP que seriam inviáveis só com nossa LAN. Você pode rodar um blog estático, um gerenciador de senhas, ou um servidor de Minecraft... só se lembre de seguir as boas práticas de segurança ao longo do caminho!
Sério, auto-hospedagem pode ser bem viciante, e o Sandhole com certeza alimenta esses impulsos.█
Precisa de ajuda? Me mande uma mensagem no Mastodon ou envie um e-mail.