Pular para o conteúdo principal

Instalação do Servidor

O Servidor do TrustBridge é distribuído como uma imagem de container Linux, trustbridge-server:<version>, com duas formas de hospedagem suportadas. As duas executam exatamente uma instância.

Docker no Linux (esta página)Azure App Service
Banco de dadosSQL Server, ao lado do Servidor ou uma instância que você já operaAzure SQL, com identidade gerenciada e sem senha
TLSO Servidor termina o próprioA plataforma o termina
Cofre de CertificadosDisponívelDisponível
Quem programa um reinícioVocêA plataforma, para a própria manutenção

A Lacuna Software fornece um pacote de implantação com a referência da imagem, o arquivo compose (docker-compose.yml), um exemplo de ambiente comentado (.env.example) e os modelos de Diretiva de Grupo para a frota. As instruções abaixo supõem que você tem esse pacote no host de destino.

Antes de escolher a forma de hospedagem​

Quatro coisas valem para toda implantação e determinam como você a verifica.

Uma instância é uma regra, não uma recomendação. As estações de trabalho mantêm uma conexão SignalR persistente e não há backplane, então uma segunda instância não sabe nada das estações de trabalho conectadas à primeira. A falha é silenciosa: as sessões falham contra máquinas que estão conectadas e invisíveis. Nunca escale horizontalmente.

Toda implantação precisa de um Protetor do Chaveiro. Os dois chaveiros (key rings) do Servidor, o que está por trás dos cookies do console administrativo e o que está por trás do Cofre de Certificados, são linhas no próprio banco de dados dele, seladas sob um único certificado X.509 que você fornece como configuração (KeyRingProtector:Certificate). O Servidor se recusa a iniciar sem ele, com ou sem cofre. Faça backup do PKCS#12 permanentemente, em algum lugar que a plataforma não consiga acessar. Veja Segurança.

O Servidor atualiza o próprio esquema. A cada inicialização, antes de começar a escutar, ele aplica ao banco de dados as migrações pendentes (Database:MigrateOnStart, padrão true). Uma implantação é uma imagem e uma connection string; uma atualização é uma nova tag de imagem. O custo disso é uma permissão permanente de DDL (db_ddladmin) para a identidade com que o Servidor se conecta, ou o interruptor definido como false e um DBA aplicando as migrações antes.

O banco de dados estáO que o Servidor faz
AtualizadoInicia. Nenhum DDL é executado.
AtrasadoAplica as migrações que faltam, registra os nomes delas no log e então inicia.
Atrasado, com MigrateOnStart=falseRecusa-se a iniciar, citando as migrações e o comando a executar.
À frente deste build (um rollback que atravessa uma migração)Recusa-se a iniciar. Código antigo sobre um esquema mais novo responderia errado em vez de falhar.
InacessívelAguarda cerca de noventa segundos e então se recusa a iniciar.

Cada comando de migração tem um limite próprio de trinta minutos; nada limita uma atualização como um todo.

"Iniciou" não é "funciona". Um Servidor em execução responde GET / com 200 mesmo com o banco de dados inacessível, de propósito, para que uma instabilidade do banco não seja uma indisponibilidade que alguém precise encerrar. A verificação que toca o banco de dados é GET /healthz: ela faz uma ida e volta ao SQL e responde Healthy (200) ou Unhealthy (503), não exige credencial e não grava nenhuma linha na Trilha de Auditoria. Aponte o seu monitoramento para ela.

Pré-requisitos​

  • Um host Linux com Docker Engine e o plugin Compose.
  • Um certificado TLS em PKCS#12 cuja cadeia todas as estações de trabalho já considerem confiável. O Workstation Service recusa uma cadeia não confiável, e o sintoma são estações de trabalho que nunca aparecem, sem nenhum erro no Servidor. Um certificado autoassinado não serve. Use a sua AC interna ou uma pública.
  • Um Protetor do Chaveiro, um segundo PKCS#12, bem diferente do primeiro. O passo 3 abaixo o cria.
  • Credenciais do Lacuna Signer: o endereço base e a chave de API no formato application|secret que o Signer publica. As duas ou nenhuma. Um endereço sem chave impede a inicialização.
  • A licença do Lacuna PKI SDK, somente se você for ligar o Cofre de Certificados. A imagem não traz licença.
  • Um SQL Server. O arquivo compose pode executar um ao lado do Servidor, mas leia a observação sobre a edição dele.

1. O diretório do host​

Um diretório, que o Servidor apenas lê, como uid 1654:

sudo mkdir -p /etc/trustbridge/tls /opt/trustbridge

O Servidor não grava nada no seu disco. O único diretório em que ele grava é /tmp, dentro do container, com os caches do PKI SDK de emissores e LCRs baixados (/tmp/trustbridge-pki) e o próprio arquivo de log rotativo (/tmp/trustbridge-logs). Os dois são caches de algo que já está registrado em outro lugar, e perdê-los não custa nada além de latência e das linhas da tela Log do Servidor. Nada ali precisa de backup.

2. TLS​

Não há nada na frente deste Servidor, então ele termina o próprio TLS:

sudo cp your-certificate.pfx /etc/trustbridge/tls/server.pfx
sudo chown 1654:1654 /etc/trustbridge/tls/server.pfx
sudo chmod 400 /etc/trustbridge/tls/server.pfx

O container escuta na 8443; o host publica a 443. Um processo sem privilégios não consegue abrir a porta 443, então o dockerd a abre e encaminha. Esse mapeamento já está no arquivo compose.

Se preferir terminar o TLS em um proxy reverso que você já opera, aponte-o para a porta 8443 do container e remova as variáveis Kestrel__Certificates__* do arquivo compose, mas então defina também ASPNETCORE_FORWARDEDHEADERS_ENABLED=true, ou toda linha de auditoria vai registrar o endereço do seu proxy como o de quem chamou.

3. Configuração​

cd /opt/trustbridge
cp <package>/docker-compose.yml .
cp <package>/.env.example .env
chmod 600 .env

O arquivo compose deve ficar no controle de versão e não contém nenhum segredo; o .env contém todos os segredos e não pode ser versionado. A página Configuração do Servidor lista todas as chaves que o Servidor aceita; as que esta forma de hospedagem exige estão abaixo.

Variável do .envO que é
TRUSTBRIDGE_IMAGEA imagem, com uma tag específica. Nunca latest.
MSSQL_SA_PASSWORDA senha do sa do SQL Server incluído (8 ou mais caracteres de três classes de caracteres).
TLS_CERT_PASSWORDA senha de /etc/trustbridge/tls/server.pfx.
KEY_RING_PROTECTOR_CERTIFICATEO Protetor do Chaveiro, uma linha de base64. Obrigatório.
KEY_RING_PROTECTOR_PASSWORDSomente se esse PKCS#12 tiver senha.
VAULT_ENABLEDfalse, a menos que este Servidor vá guardar Certificados do Cofre.
PKI_LICENSEA licença do Lacuna PKI SDK, em base64 em uma linha. Necessária com o cofre ligado.
SIGNER_BASEADDRESS, SIGNER_APIKEYO Lacuna Signer. As duas ou nenhuma.
ADMIN_BOOTSTRAP_EMAIL, ADMIN_BOOTSTRAP_PASSWORDO primeiro administrador. Lidas somente enquanto a tabela de contas estiver vazia; remova-as depois da primeira entrada.

Usando um SQL Server que você já opera​

Essa é a forma normal em produção. Exclua o serviço sqlserver do arquivo compose, remova o depends_on e aponte ConnectionStrings__TrustBridge para a instância real:

ConnectionStrings__TrustBridge: >-
Server=sql.example.com,1433;Database=TrustBridge;User Id=trustbridge;
Password=${DB_PASSWORD};Encrypt=True

A identidade precisa de db_datareader, db_datawriter e, para que o Servidor aplique as próprias migrações, db_ddladmin. O banco de dados em si precisa existir. Aplicar uma migração nunca cria um banco de dados, exceto na forma com banco incluído, em que o Servidor se conecta como sa e pode criá-lo.

cuidado

Se você mantiver o banco de dados incluído, note que o arquivo compose define MSSQL_PID=Developer, que a Microsoft licencia somente para desenvolvimento e teste, não para produção. Defina uma edição para a qual você tenha licença, ou use um SQL Server que a sua organização já opera e do qual já faz backup. Nada vai avisar você.

O Protetor do Chaveiro​

Crie um. Um certificado RSA autoassinado é o formato certo: nada nunca o valida como certificado, então não há nada que uma AC precise atestar. Ele é um par de chaves com uma impressão digital (thumbprint).

openssl req -x509 -newkey rsa:2048 -nodes -days 18250 \
-keyout protector.key -out protector.crt \
-subj "/CN=TrustBridge Key Ring Protector"
openssl pkcs12 -export -inkey protector.key -in protector.crt -out protector.pfx -passout pass:
base64 -w0 protector.pfx
  • RSA, não ECDSA. O framework sela o material de chave por meio de EncryptedXml, que só usa RSA. Um certificado ECDSA é recusado na inicialização.
  • Cinquenta anos de validade. As datas de um protetor nunca são verificadas, mas um protetor que você se sinta obrigado a substituir é um que, de qualquer forma, terá de guardar para sempre.
  • base64 -w0: uma linha, sem cabeçalho PEM, que é o que a configuração lê.
  • Sem senha é a escolha honesta: o único lugar para guardá-la seria o mesmo .env que contém os bytes que ela abre.

Cole o base64 no .env como KEY_RING_PROTECTOR_CERTIFICATE. Depois, coloque protector.pfx em algum lugar que este host não consiga acessar e remova-o do host:

shred -u protector.key protector.pfx # only after copying the .pfx somewhere safe
perigo

Perder o protetor destrói o Cofre de Certificados, e nada o recupera. Nem a Lacuna, nem você. Não há custódia de recuperação (escrow), de propósito. É um arquivo pequeno que nunca muda: guarde-o onde a sua organização guarda aquilo que não pode recriar.

A rotação é aditiva. Veja Operações.

Ligando o Cofre de Certificados​

VAULT_ENABLED=false é o padrão e a forma mais comum: toda chave privada fica em uma estação de trabalho. Defina true somente em um Servidor que vá guardar Certificados do Cofre, e defina PKI_LICENSE junto. Leia antes Segurança: com o recurso ligado, a licença do PKI SDK passa a ser crítica, e um PKCS#12 a importar precisa estar no formato PFX legado.

echo "PKI_LICENSE=$(base64 -w0 LacunaPkiLicense.config)" >> .env

4. O esquema​

Nada a fazer. O Servidor cria e migra o próprio esquema na inicialização. Pule para o passo 5.

Há dois motivos para fazer isso você mesmo: o seu controle de mudanças não concede DDL ao Servidor em execução, ou um DBA quer ler o SQL antes. Em qualquer um dos casos, defina Database__MigrateOnStart: "false" no arquivo compose e, antes de cada inicialização em uma versão nova, aplique as migrações com o .NET SDK e uma cópia do código na versão implantada, ou gere um script idempotente e aplique-o com o sqlcmd:

dotnet tool restore
dotnet ef migrations script --idempotent --project src/TrustBridge.Server -o schema.sql
sqlcmd -S <host> -U <user> -P <password> -C -I -b -d TrustBridge -i schema.sql

O -I é obrigatório (QUOTED_IDENTIFIER ON; sem ele, o primeiro índice filtrado falha com Msg 1934), e o -b também (sem ele, uma aplicação parcial informa sucesso). Se você executar dotnet ef database update em vez disso, acrescente Command Timeout=3600 à connection string: o padrão da ferramenta é trinta segundos, menos do que leva a criação de um índice sobre uma tabela AuditEvents grande.

Com o interruptor desligado, o Servidor se recusa a iniciar quando há uma migração pendente, em vez de iniciar sem ela. Esse é o objetivo do interruptor.

5. Inicie o Servidor​

cd /opt/trustbridge && docker compose up -d
docker compose logs -f server

Uma primeira inicialização saudável:

info: TrustBridge.Server.Common.KeyRings.KeyRingPosture[0]
Every key ring this Server keeps in its database is encrypted with the Key Ring Protector
76766DC2741A8DA070CC47E3830D34023D9ED885, and 0 retired protector(s) are kept readable beside
it. Losing a protector makes every key ever sealed under it — and so every Vault Certificate
it wrapped — unrecoverable, so backing them all up, permanently, is part of running this
Server (ADR-0024).
info: TrustBridge.Server.Common.Vault.VaultReadiness[0]
The Certificate Vault is off on this Server, so it holds no certificates of its own. Every
private key it signs with is on a workstation (ADR-0020).
info: TrustBridge.Server.Common.Admin.AdminAccountBootstrap[0]
Created the first admin account admin@example.com in the Administrator role.
info: Now listening on: https://[::]:8443
info: Application started. Press Ctrl+C to shut down.

A primeira linha é a que deve ser lida. Ela informa a impressão digital do protetor que o seu backup precisa conter e conta os protetores aposentados com que este Servidor ainda consegue ler. Se você fez uma rotação e ela diz 0 retired protector(s), o protetor antigo não está configurado e tudo o que foi selado sob ele está ilegível.

Com o cofre ligado, a segunda linha passa a ser The Certificate Vault is open: its key ring holds N key(s)…. Um warn no lugar dela dizendo que o cofre está closed until they have run significa que o Servidor subiu antes do banco de dados. Essa é a trava do cofre.

6. Verifique se ele realmente funciona​

curl -s https://trustbridge.example.com/healthz # Healthy
curl -o /dev/null -w '%{http_code}\n' https://trustbridge.example.com/ # 200 front door
curl -o /dev/null -w '%{http_code}\n' https://trustbridge.example.com/admin # 302 to login
curl -o /dev/null -w '%{http_code}\n' https://trustbridge.example.com/api/v1/sessions # 401 no API key

Em seguida, entre em /admin com a conta inicial. Isso lê o banco de dados pelo caminho que uma requisição real percorre, e não pelo de um probe. Defina uma senha real, cadastre o segundo fator e então remova ADMIN_BOOTSTRAP_EMAIL e ADMIN_BOOTSTRAP_PASSWORD do .env e recrie o container. Elas não têm efeito depois da primeira inicialização; removê-las é uma questão de higiene.

Por fim, registre uma Aplicação Cliente em /admin/applications, emita uma chave de API e exercite a API REST uma vez, de ponta a ponta.

Backups​

O quêComoSe você perdê-lo
O banco de dadosBACKUP DATABASE, ou os backups de SQL Server que você já fazSessões, Trilha de Auditoria, Registro de Certificados, os dois chaveiros e o texto cifrado do cofre
O .pfx do Protetor do ChaveiroCopie o arquivo uma vez. Ele nunca mudaNenhum Certificado do Cofre pode mais ser aberto, permanentemente, e todos os administradores entram de novo
O .pfx de cada protetor aposentadoDa mesma forma, guardado para sempreTudo o que foi selado sob aquele protetor, permanentemente
/etc/trustbridge/tlsOnde quer que os seus certificados fiquemEmita de novo
O log do container, para onde quer que o seu logging driver o envieA sua retenção de logs, não uma cópia de arquivoLinhas de auditoria que o banco de dados não aceitou. A trilha tem um buraco exatamente onde o banco esteve fora do ar

O logging driver json-file do arquivo compose guarda as linhas no diretório do próprio container, e o Docker as apaga junto com o container; então docker compose down e a atualização descrita abaixo as descartam. Aponte o driver de logging: para algo fora desta máquina, ou execute um agente de log sobre os arquivos, antes de precisar deles.

Restaurar exige que duas coisas cheguem juntas. Um banco de dados sem o protetor dele impede a inicialização; um protetor sem o banco de dados inteiro (linhas do cofre presentes, linhas do chaveiro ausentes) é recusado pelo motivo simétrico.

Atualizações​

docker compose pull && docker compose up -d

A imagem nova aplica as migrações de que precisa ao subir e registra os nomes delas no log. Se você definiu Database__MigrateOnStart: "false", aplique as migrações antes; a imagem nova se recusa a iniciar contra um esquema atrasado.

Conte com uma indisponibilidade visível de um ou dois minutos: uma instância significa que não há rolling restart. As estações de trabalho se reconectam sozinhas, e as sessões vinculadas a estações de trabalho sobrevivem intactas. As sessões vinculadas a um Certificado do Cofre, não; elas terminam como Interrompida e os clientes criam novas.

Atualize primeiro o Servidor, depois as estações de trabalho, dentro de um mesmo ciclo de versão. Um Servidor aceita o protocolo atual das estações de trabalho e o anterior; uma estação de trabalho à frente do Servidor não consegue se conectar. Veja Operações.

Um banco de dados criado pelo Servidor 1.2.0 ou anterior não pode ser atualizado: o histórico de migrações foi consolidado em uma única linha de base depois disso, e um banco assim é recusado na inicialização como um que tem migrações que este build não contém. Descarte-o e deixe o Servidor criar um novo.

Quando o Servidor não inicia​

O log dizCausa
KeyRingProtector:Certificate is not configuredNão há protetor. Toda implantação precisa de um.
… is not base64, so it cannot be a PKCS#12O valor tem um cabeçalho PEM ou quebras de linha. Use base64 -w0.
… could not be opened as a PKCS#12CertificatePassword errada, ou os bytes não são um PKCS#12. O Servidor não consegue distinguir os dois casos.
… carries no RSA private key this process can useUm certificado ECDSA, ou um exportado sem a chave.
The Certificate Vault's key ring holds N key(s) and the Key Ring Protector configured here opens none of themUm protetor trocado. Recoloque o antigo, ou adicione-o em RetiredCertificates.
The Certificate Vault holds N certificate(s) and its key ring is emptyUma restauração parcial. Restaure o banco de dados inteiro.
Signer:ApiKey is required when Signer:BaseAddress is configuredSigner configurado pela metade. Defina os dois ou nenhum.
… is behind the build pointed at it by N migration(s)MigrateOnStart está desligado e o esquema precisa dessas migrações.
… has N migration(s) applied that this build does not containUma imagem mais antiga contra um esquema mais novo, ou um banco de dados da 1.2.0 ou anterior.
… could not be reached in 90 secondsO banco de dados nunca respondeu. Verifique a connection string e se o SQL Server está no ar.
… could not apply N pending migration(s)Em geral, falta de permissão de DDL para a identidade que se conecta.
… ran out of time applying N pending migration(s)Um comando de migração passou de trinta minutos. Nada ficou aplicado pela metade.
Inicia, mas as estações de trabalho nunca se conectamAs estações de trabalho não confiam na cadeia TLS.

Duas linhas do framework parecem falhas do protetor e não são: KeyRingProvider[48] An error occurred while reading the key ring e XmlKeyManager[24] … Unable to retrieve the decryption key. Em um Servidor que iniciou sem erros, elas significam que o banco de dados está inacessível. As recusas do próprio protetor sempre citam KeyRingProtector: e param o host.

Ver em Markdown