Esse artigo faz um apanhado do conhecimento, informações e instruções de como implementar o Harbor em um ambiente de containers Docker, orquestrados com Kubernetes.
Introdução
É interessante observar o que diz no site oficial do Harbor:
“Our mission is to be the most secure, performant, scalable, and available cloud native repository for Kubernetes” - Nossa missão é ser o repositório cloud native mais seguro, com melhor desempenho, escalável e disponível para o Kubernetes (Tradução Livre)
Ser um Docker Registry é uma das features mais maduras e mais completas do Harbor, mas suas possibilidades vão além disso! Segundo a documentação, é possível também utiliza-lo como um repositório para Charts Helm (ChartMuseum), fazer replicas em Registries As Service em provedores de Clouds públicas, Criação de Jobs automatizados para Image deletion & garbage collection, RBAC (estrutura de permissões, usuários, grupos e projetos), Integração com ActiveDirectory/LDAP/OIDC, Scan de Vulnerabilidades (Clair) e integrar com diversas soluções de segurança (OPA, Aqua Security, HashiCorp Vault, entre outras).
Pretty cool, huh? 🙃
Arquitetura
De todos os que vi, acredito que o diagrama abaixo é o que descreve melhor a atual arquitetura do Harbor. (versão 1.10 até a criação desse artigo)
Assim como a observação na figura, é interessante ressaltar que o Harbor possui diversos componentes OpenSource, havendo a possibilidade de desacoplamento e utilização desses componentes “As Services” nos provedores de cloud.
Instalação
O Harbor disponibiliza um Chart Helm oficial para a sua instalação. O que é possível adicionar ao helm local pelo comando abaixo:
helm repo add harbor https://helm.goharbor.io
É possível encontrar o arquivo values.yaml default no repositório do Chart, mas eu irei comentar abaixo os campos e configurações mais importantes para uma instalação inicial.
Exposição do Serviço
É possível expor o Harbor nas estratégias mais tradicionais do Kubernetes (ingress, clusterIp, nodePort, loadBalancer). Por padrão é utilizando a estratégia de Ingress. Nesse caso é necessário indicar as configurações do Ingress Controller já existente no ambiente, ou um serviço do provedor de Cloud Computing. É possível ajustar essas configurações no bloco “expose:{}” de acordo com a estratégia escolhida.
Caso não utilize a opção “ingress”, será provisionado um Nginx Ingress Controller. É possível customizar as configuração desse serviço no bloco “nginx:{}”, recomendo utilizar o values.yaml padrão como referência.
URLs
Outro ponto fundamental é indicar um valor para “externalURL:{}”. Segundo a documentação, essa URL será utilizada pelo serviço core do Harbor para:
1) populate the docker/helm commands showed on portal
2) populate the token service URL returned to docker/notary client
O formato é bem padrão para URLs:
protocol://domain[:port]
Boas práticas indicadas nas documentações:
1) Se “expose.type” for “ingress”, o “domain” deve ter o valor “expose.ingress.hosts.core”
2) Se “expose.type” for “clusterIP”, o “domain” deve ter o valor “expose.clusterIP.name”
3) Se “expose.type” for “nodePort”, o “domain” deve ter o valor endereço IP do node k8s.
# Se o Harbor for instalado pos trás de um proxy, configure o “domain” como a URL ou IP do proxy.
Também é necessário indicar a URLs para o serviço Notary e Core, irei falar mais sobre eles, mas é necessário indicar as URLs em “expose.ingress.hosts.core:{}” e “expose.ingress.hosts.notary:{}”
TLS
Especialmente para o serviço de Registry, a configuração de TLS é fundamental. Caso não exista implementação do TLS em um Ingress Controller próprio, ou utilize um domínio especifico para o Harbor, é necessário indicar um secret que contenha os arquivos *.crt e *.key no campo “expose.tls.secretName:{}”
Persistência
O Harbor possui compatibilidade com diversos tipos de storages, sendo possível configurar essas opções no bloco “persistence:{}”, indicando configurações de PVCs para cada serviço que possua persistências (Redis, Registry, Charmuseum e etc).
Data Storage (PVCs/PVs)
Observe que, por padrão, o campo “persistence.resourcePolicy:{}” tem o valor “keep”, para que os PVCs não sejam removidos quando o chart helm for excluído. Isso levanta um ponto importante para a preservação dos dados, nos testes que fiz o valor padrão “keep” para esse campo de fato mantém os PVCs durante a exclusão do Chart. Entretanto, ao fazer a reinstalação do chart, o Helm retorna erro informando que os volumes já existem. A estratégia que adotei foi criar os PVCs na instalação (ou manualmente) e indicar seus respectivos nomes no values.yaml. Desta forma a reinstalação acontece sem problemas e os dados ficam intactos.
Outros tipos de Backends
Também é possível utilizar outros tipos de “backends” de armazenamentos além do padrão, que é o “filesystem” com PVCs. Em “persistence.imageChartStorage:{}” estão os campos para configurar outros backends, como s3, azure, gcs, swift e outros.
Database (PostgreSQL / Redis)
O Harbor necessita armazenar os metadados relacionados aos seus objetos, como projetos, usuários, funções, políticas de replicação, políticas de retenção de tags, scanners, gráficos e imagens. Para tal é utilizado o PostgreSQL. O chart contempla a instalação de uma imagem própria (“goharbor/harbor-db”) do PostgreSQL pelo controller Statefullset. É possível customizar suas configurações dentro do bloco “database:{}”, sendo possível também a utilização de um banco externo, bastando indicar o valor “external” em “database.type{}” e passar os dados de acesso dentro do bloco “database.external{}”.
É possível fazer o mesmo com o Redis, dentro do bloco “redis:{}”.
Serviços Fundamentais
Conforme comentei na abordagem da arquitetura, o Harbor possui alguns serviços internos, eles são responsáveis por funcionalidades especificas e possuem blocos de configuração dentro do values.yaml.
O Core é um serviço responsável por Autenticação, gerenciamento de configuração, de cotas, de projetos e dentre muitas outras features. Mas não é possível configurar nada disso pelo Chart Helm, apenas na interface após a instalação do Harbor. Isso me preocupa um tanto por questões de resiliência, pois as configurações estarão dentro do banco de dados, sendo necessário redobrar atenção sobre ele.
É possível realizar configurações básicas, quanto a implementação do core, dentro do bloco “core:{}” no values.yaml
Assim como o core, o mesmo vale para os demais serviços. As configurações possíveis no values.yaml são básicas, voltadas apenas para a implementação das aplicações.
Interface Gráfica
Um dos pontos mais interessantes do Harbor é disponibilizar uma interface gráfica bem completa, o que entrega ao usuário maior autonomia e confiança durante o uso das features.
Harbor e Imagens Docker
A interação com o Harbor via CLI funciona como qualquer registry docker (Docker Hub por exemplo), com as possibilidade de PUSH e PULL tradicionais. Os pontos de atenção ficam para as features de gestão e segurança das imagens hospedadas nele.
Assim que uma imagem é adicionada ao Harbor, ela passar por Scans de Segurança que relacionam CVE abertas para vulnerabilidades encontradas na imagem. O Harbor faz uso de soluções pré-disponíveis na instalação, como o Clair e/ou Trivy.
Scans de Vulnerabilidade (Clair / Trivy / Notary)
Clair é um projeto Open Source da CoreOS para a análise estática de vulnerabilidades em containers appc e docker.
Trivy (tri-pronunciado como trigger, vy pronunciado como envy) é um scanner de vulnerabilidade simples e abrangente para containers. Atualmente o Trivy faz parte da stack da Aqua Security, mas é possível utiliza-lo individualmente, ou embarcado em soluções como o Harbor.
Tanto o Clair quanto o Trivy detectam vulnerabilidades de pacotes de SO (Alpine, RHEL, CentOS etc.) e dependências de aplicativos (Bundler, Composer, npm, yarn etc.).
No canal oficial do Harbor tem uma demonstração sobre o uso desse Scans.
Imagens Docker Assinadas (Notary)
Uma estratégia importante é “Docker Content Trust”, ela é citada na documentação do docker sob a indicação de uso com o Notary. O Notary é uma ferramenta para entregar e gerenciar coleções de conteúdos confiáveis. A premissa é que uma imagem seja assinada digitalmente e quem a consuma pode verificar a integridade e a origem do conteúdo. Esse recurso é desenvolvido em uma interface direta de gerenciamento e assinatura de chaves para criar coleções assinadas e configurar editores confiáveis.
A operação mais básica é listar as tags assinadas em um repositório. A documentação do Docker mostra um exemplo básico, onde é possível correlacionar algumas imagens presentes no Docker Hub com assinaturas que já existem no servidor Notary público.
É possível entender melhor a arquitetura desse serviço pela documentação, é uma boa leitura.
Conclusão
Existem muitas soluções interessantes para repositório de imagens, mas o Harbor se apresenta como um dos mais completos. Sua estrutura, instalação e as ferramentas que o compõe refletem uma natureza Cloud Native, sem reinventar a roda, utilizando soluções consolidadas no mercado para tarefas de armazenamento e segurança. Isso ajuda muito para traçar uma estratégia de gestão e alta disponibilidade em cima do Harbor.
Autor: Bruno S. Brasil