Do commit ao cluster, com tudo à vista.
From commit to cluster, with everything in sight.
CommitKube é uma plataforma DevOps e de segurança para quem roda serviços no Kubernetes. Cria o repositório e a pipeline, entrega com GitOps e depois acompanha o que subiu: saúde dos workloads, erros, vulnerabilidades, postura do cluster, métricas DORA e a borda na Cloudflare.
CommitKube is a DevOps and security platform for teams running services on Kubernetes. It creates the repository and the pipeline, delivers with GitOps, then follows what shipped: workload health, errors, vulnerabilities, cluster posture, DORA metrics and your Cloudflare edge.
docker pull commitkube/commitkube:latestUma plataforma no lugar de oito ferramentas
One platform instead of eight tools
Cada módulo lê o que o cluster e o repositório já dizem. Nenhum exige um agente rodando ao lado da sua aplicação.
Every module reads what the cluster and the repository already say. None of them needs an agent running next to your application.
Repositórios e entrega
Repositories and delivery
Cria o repositório com estrutura padronizada, pipeline, variáveis e deploy key.
Creates the repository with a standard layout, pipeline, variables and a deploy key.
- Navegador de código com edição e commit
- Pipelines: disparar, parar, ver steps e logs
- Templates e golden paths com regras e aprovação
- Code browser with editing and commits
- Pipelines: trigger, stop, see steps and logs
- Templates and golden paths with rules and approval
GitOps próprio
Built-in GitOps
Qualquer repositório Git por SSH, mantido aplicado no cluster.
Any Git repository over SSH, kept applied to the cluster.
- YAML puro, Kustomize e Helm
- Diff pelo dry-run do próprio API server
- Auto-sync, self-heal, prune e webhook de push
- Plain YAML, Kustomize and Helm
- Diff from the API server's own dry-run
- Auto-sync, self-heal, prune and push webhooks
Kubernetes
Overview do cluster, navegador de recursos e ações do dia a dia.
Cluster overview, a resource browser and day-to-day actions.
- Pods com CPU, memória, throttling e logs ao vivo
- ReplicaSets: limpeza em lote das revisões antigas
- Multi-cluster e impersonation
- Pods with CPU, memory, throttling and live logs
- ReplicaSets: bulk cleanup of old revisions
- Multi-cluster and impersonation
Triage
Problemas com ciclo de vida: abrem uma vez, acumulam ocorrências e fecham sozinhos.
Problems with a lifecycle: they open once, count occurrences and close on their own.
- OOMKilled, CrashLoop, image pull, evicted
- 5xx do ingress e erros de log agrupados
- Service map das dependências declaradas
- OOMKilled, CrashLoop, image pull, evicted
- Ingress 5xx and grouped log errors
- Service map of declared dependencies
Segurança
Security
Trivy no código e na imagem que está rodando, com histórico e triagem.
Trivy on the code and on the image that is running, with history and triage.
- CVEs, misconfiguração e segredos vazados
- Riscos de workload e postura do cluster (33 regras)
- Comando de correção gerado do objeto vivo
- CVEs, misconfigurations and leaked secrets
- Workload risks and cluster posture (33 rules)
- Fix commands generated from the live object
Entrega (DORA)
Delivery (DORA)
Medida pelo cluster, não pela pipeline: um deploy é uma imagem que passou a rodar.
Measured from the cluster, not the pipeline: a deploy is an image that started running.
- Frequência e lead time do commit até o pod
- Change failure rate pela saúde pós-deploy
- Tempo de recuperação
- Frequency and lead time from commit to pod
- Change failure rate from post-deploy health
- Time to recovery
Cloudflare
Analytics, ataques, bots e o que fica exposto por baixo do proxy.
Analytics, attacks, bots and what is exposed underneath the proxy.
- Sugestões de regras de WAF a partir do tráfego real
- Origem exposta, portas abertas, subdomain takeover
- Alertas que fecham sozinhos
- WAF rule suggestions from real traffic
- Exposed origins, open ports, subdomain takeover
- Alerts that close on their own
Vault
Cofre com criptografia ponta a ponta no navegador. O servidor só guarda blobs selados.
A vault with end-to-end encryption in the browser. The server only stores sealed blobs.
- Argon2id, P-256 (ECDH) e AES-GCM
- Cofres pessoais e de time
- Segundo TOTP para abrir material selado
- Argon2id, P-256 (ECDH) and AES-GCM
- Personal and team vaults
- A second TOTP to open sealed material
Acesso e auditoria
Access and audit
MFA desde o primeiro acesso e permissões que o servidor exige em cada rota.
MFA from the first login, and permissions the server enforces on every route.
- Sessão que morre com o navegador
- Escopo por namespace e RBAC gerado
- Audit log e notificações (Teams, webhook, e-mail)
- Sessions that end with the browser
- Namespace scoping and generated RBAC
- Audit log and notifications (Teams, webhook, email)
Conecte um repositório e mantenha o cluster igual ao Git
Connect a repository and keep the cluster matching Git
Funciona com qualquer host Git que fale SSH: GitHub, GitLab, Bitbucket, Gitea ou um servidor próprio.
Works with any Git host that speaks SSH: GitHub, GitLab, Bitbucket, Gitea or your own server.
Conecte por SSH
Connect over SSH
O CommitKube gera um par de chaves e mostra a pública para virar deploy key. Se preferir, cole uma chave que você já tem. A chave do host fica fixada na primeira conexão.
CommitKube generates a key pair and shows the public key to add as a deploy key, or you paste one you already have. The host key is pinned on the first connection.
Escolha o caminho
Pick the path
Um navegador de pastas mostra o que cada uma contém: manifestos, overlay Kustomize ou chart Helm. Filtros de include e exclude por glob.
A folder browser shows what each folder holds: manifests, a Kustomize overlay or a Helm chart. Include and exclude filters by glob.
Veja o diff
Review the diff
O próprio API server faz o dry-run do server-side apply. Um manifesto inválido volta com o erro antes de qualquer escrita.
The API server itself dry-runs the server-side apply. An invalid manifest comes back with its error before anything is written.
Sincronize
Sync
Auto-sync a cada commit, self-heal contra edições manuais e prune do que saiu do Git. O webhook de push atualiza na hora.
Auto-sync on every commit, self-heal against manual edits and prune of what left Git. A push webhook refreshes right away.
Tudo num pod só
Everything in one pod
O nginx na porta 80 encaminha /api para a API em Go e o resto para a interface em Next.js. Em background, a mesma API cuida dos scans e dos pollers.
nginx on port 80 sends /api to the Go API and everything else to the Next.js interface. In the background, the same API runs the scans and the pollers.
SQLite em volume persistente. Um PVC em /app/data guarda o banco e o cache do Trivy.
SQLite on a persistent volume. A PVC at /app/data holds the database and the Trivy cache.
Ferramentas com versão fixa. Trivy, Helm e Kustomize entram na imagem com checksum conferido no build.
Pinned tools. Trivy, Helm and Kustomize go into the image with checksums verified at build time.
Permissões explícitas. Um ClusterRole diz o que a plataforma pode ler. Corte o que não quiser expor.
Explicit permissions. A ClusterRole states what the platform may read. Cut whatever you don't want to expose.
Subindo no Kubernetes
Running on Kubernetes
Cinco passos com kubectl. Os exemplos usam o namespace commitkube; se usar outro, troque em todos os arquivos.
Five steps with kubectl. The examples use the commitkube namespace; if you use another one, change it in every file.
Namespace e segredos
Namespace and secrets
JWT_SECRET assina as sessões e ENCRYPTION_KEY cifra as credenciais no banco. Gere as duas antes da primeira subida.
JWT_SECRET signs sessions and ENCRYPTION_KEY encrypts the credentials in the database. Generate both before the first start.
kubectl create namespace commitkube kubectl -n commitkube create secret generic commitkube-secrets \ --from-literal=JWT_SECRET=$(openssl rand -hex 32) \ --from-literal=ENCRYPTION_KEY=$(openssl rand -hex 32) # guarde uma cópia da ENCRYPTION_KEY fora do cluster# keep a copy of ENCRYPTION_KEY outside the cluster kubectl -n commitkube get secret commitkube-secrets \ -o jsonpath='{.data.ENCRYPTION_KEY}' | base64 -d; echo
ServiceAccount e permissões
ServiceAccount and permissions
O ClusterRole abaixo cobre todos os módulos. Cada regra é opcional. Sem secrets, a página de Secrets fica vazia. Sem nodes/proxy, não há detecção de throttling de CPU. Sem configmaps, o service map perde a maioria das arestas.
The ClusterRole below covers every module, and each rule is optional. Without secrets, the Secrets page is empty. Without nodes/proxy, there is no CPU throttling detection. Without configmaps, the service map loses most of its edges.
›rbac.yaml
apiVersion: v1 kind: ServiceAccount metadata: name: commitkube namespace: commitkube --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: commitkube rules: # Leitura do cluster: overview, navegador, triage, service map# Cluster reads: overview, browser, triage, service map - apiGroups: [""] resources: ["pods", "nodes", "namespaces", "services", "endpoints", "persistentvolumeclaims", "configmaps"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["resourcequotas", "limitranges", "serviceaccounts"] verbs: ["get", "list"] - apiGroups: ["apps"] resources: ["deployments", "replicasets", "daemonsets", "statefulsets"] verbs: ["get", "list", "watch"] - apiGroups: ["batch"] resources: ["cronjobs", "jobs"] verbs: ["get", "list", "watch"] - apiGroups: ["networking.k8s.io"] resources: ["ingresses", "ingressclasses", "networkpolicies"] verbs: ["get", "list", "watch"] # Métricas: metrics-server e o cadvisor de cada kubelet# Metrics: metrics-server and each kubelet's cadvisor - apiGroups: ["metrics.k8s.io"] resources: ["pods", "nodes"] verbs: ["get", "list"] - apiGroups: [""] resources: ["nodes/proxy", "nodes/metrics", "pods/log"] verbs: ["get"] # Ações: restart, scale, limpeza de ReplicaSets# Actions: restart, scale, ReplicaSet cleanup - apiGroups: [""] resources: ["pods"] verbs: ["delete"] - apiGroups: ["apps"] resources: ["replicasets"] verbs: ["delete"] - apiGroups: ["apps"] resources: ["deployments/scale", "replicasets/scale", "statefulsets/scale"] verbs: ["get", "update"] # Página de Secrets (remova "update" para deixá-la só leitura)# Secrets page (drop "update" to keep it read-only) - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "update"] - apiGroups: ["external-secrets.io"] resources: ["externalsecrets", "secretstores", "clustersecretstores"] verbs: ["get", "list"] # Security Posture: avaliação das regras de RBAC# Security Posture: assessing the RBAC rules - apiGroups: ["rbac.authorization.k8s.io"] resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"] verbs: ["get", "list"] # Istio (opcional)# Istio (optional) - apiGroups: ["networking.istio.io"] resources: ["virtualservices"] verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: commitkube roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: commitkube subjects: - kind: ServiceAccount name: commitkube namespace: commitkube
O GitOps escreve no cluster, então precisa de mais permissão. A tela de cada aplicação mostra o RBAC exato que falta, por namespace ou para o cluster todo.
GitOps writes to the cluster, so it needs more than this. Each application's page shows the exact RBAC it is missing, per namespace or cluster-wide.
Volume, Deployment e Service
Volume, Deployment and Service
Use uma réplica só, porque o banco é SQLite. Use strategy: Recreate: o volume é ReadWriteOnce, e num rolling update o pod novo fica preso esperando o antigo liberar o disco.
Run a single replica, since the database is SQLite. Use strategy: Recreate: the volume is ReadWriteOnce, and in a rolling update the new pod gets stuck waiting for the old one to release the disk.
›commitkube.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: commitkube-data
namespace: commitkube
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: commitkube
namespace: commitkube
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels: { app: commitkube }
template:
metadata:
labels: { app: commitkube }
spec:
serviceAccountName: commitkube
containers:
- name: commitkube
image: commitkube/commitkube:v1.0.6
ports:
- containerPort: 80
envFrom:
- secretRef:
name: commitkube-secrets
env:
- name: DEFAULT_CLUSTER_NAME
value: production
volumeMounts:
- name: data
mountPath: /app/data
readinessProbe:
tcpSocket: { port: 80 }
initialDelaySeconds: 5
resources:
requests: { cpu: 250m, memory: 512Mi }
limits: { memory: 2Gi }
volumes:
- name: data
persistentVolumeClaim:
claimName: commitkube-data
---
apiVersion: v1
kind: Service
metadata:
name: commitkube
namespace: commitkube
spec:
selector: { app: commitkube }
ports:
- port: 80
targetPort: 80kubectl apply -f rbac.yaml -f commitkube.yaml kubectl -n commitkube rollout status deploy/commitkube
As versões publicadas estão nas tags do Docker Hub. Prefira uma tag de versão a latest, para saber o que está rodando. O limite de 2Gi de memória dá folga para os scans do Trivy.
Published versions are listed in the Docker Hub tags. Prefer a version tag over latest, so you know what is running. The 2Gi memory limit leaves room for Trivy scans.
Exponha com um IngressExpose it with an Ingress (opcional)(optional)
Com TLS no Ingress, o cookie de sessão é marcado como Secure pelo cabeçalho X-Forwarded-Proto. Para forçar isso, defina COOKIE_SECURE=true. Troque o host e a classe pelos seus.
With TLS on the Ingress, the session cookie is marked Secure from the X-Forwarded-Proto header. To force it, set COOKIE_SECURE=true. Replace the host and the class with yours.
›ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: commitkube
namespace: commitkube
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 50m
spec:
ingressClassName: nginx
tls:
- hosts: ["commitkube.example.com"]
secretName: commitkube-tls
rules:
- host: commitkube.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: commitkube
port: { number: 80 }Para testar sem Ingress:
To try it without an Ingress:
kubectl -n commitkube port-forward svc/commitkube 8080:80 # abra http://localhost:8080# open http://localhost:8080
Primeiro acesso
First login
Entre em /login com o root criado no primeiro boot: admin@commitkube.local / admin123. Para começar com outro usuário e senha, adicione ADMIN_EMAIL e ADMIN_PASSWORD ao Secret. Em seguida, /setup pede e-mail e senha próprios e o MFA num app autenticador.
Go to /login and sign in with the root account created on first boot: admin@commitkube.local / admin123. To start with a different user and password, add ADMIN_EMAIL and ADMIN_PASSWORD to the Secret. Then /setup asks for your own email and password and for MFA in an authenticator app.
O cluster onde o CommitKube roda é registrado sozinho. Outros clusters entram depois pela interface, com o endereço do API server, a CA e um token.
The cluster CommitKube runs in is registered automatically. Other clusters are added later from the interface, with the API server address, its CA and a token.
Código e imagem
Source and image
O código está no GitHub. As imagens prontas estão no Docker Hub.
The source is on GitHub. Ready-made images are on Docker Hub.