Compare commits

..

No commits in common. "fb6395efc54975bb504215bb96a882db89c6e6a4" and "8735ceb9303f9193824029a65b4b3a53a2c37f0d" have entirely different histories.

41 changed files with 151 additions and 1023 deletions

View File

@ -1,133 +0,0 @@
# https://cspell.org/configuration/
version: "0.2"
language: en,pt
dictionaries:
- en-us
- pt-br
- softwareTerms
- bash
- networking-terms
import:
- "@cspell/dict-pt-br/cspell-ext.json"
words:
- alice
- anubis
- asciidoctor
- bioinformaticians
- bioinformatics
- Clonezilla
- Configless
- cpus
- colortheme
- ctstate
- colorthemeswitcher
- comentario
- conntrack
- chacha
- cspell
- dport
- docker
- ESXi
- fail2ban
- findtime
- filesystems
- frontmatter
- GParted
- GSSAPI
- HELO
- hreflang
- icmpv6
- iptables
- ignoreip
- ip6tables
- ipset
- jails
- jsonld
- lastmod
- linkcheck
- linkinator
- lychee
- markdownlint
- markdownlintrc
- maxretry
- mathjax
- mermaid
- nerdalert
- netcat
- noindex
- nvme
- opencode
- oque
- pagination
- PagerSize
- QUIC
- rsync
- sshaudit
- sbatch
- scancel
- scontrol
- sinfo
- slurmd
- slurmctld
- slurmdbd
- squeue
- srun
- shortcodes
- Sysadmin
- sysadmin
- textlint
- trafego
- Thanwer
- TOML
- umami
- Umami
- VMDK
- VMX
- webserver
- yamllint
- ZgotmplZ
- arancibia
- thanwer
- bantime
- bootable
- configless
- dropadas
- dropar
- dropamos
- enablecolorthemeswitcher
- Gparted
- hugo
- icds
- JOBID
- nmap
- Nmap
- NODELIST
- nodelist
- ntasks
- readnextposts
- shinobi
- Slurm
- slurm
- slurmctl
- socialicons
- TIMELIMIT
- unbootable
- Universität
- pietro
flagWords: []
ignorePaths:
- node_modules
- public
- resources
- .git
- "*.gif"
- "*.png"
- "*.jpg"
- "*.webp"
- "*.svg"
- "*.ico"
- themes
- "package.json"
- "package-lock.json"
enableFiletypes:
- markdown

View File

@ -1,14 +0,0 @@
# https://github.com/lycheeverse/lychee
exclude = [
"linkedin\\.com",
"facebook\\.com",
"instagram\\.com",
"x\\.com",
"t\\.me",
"telegram\\.me",
"youtube\\.com",
"sshaudit\\.com",
]
exclude_private = true
timeout = 20
max_retries = 2

View File

@ -1,7 +0,0 @@
linkedin\.com
facebook\.com
instagram\.com
x\.com
t\.me
telegram\.me
youtube\.com

View File

@ -1,63 +0,0 @@
# https://github.com/DavidAnson/markdownlint-cli2
frontMatter: (^\+\+\+$[^]*?^\+\+\+$)
globs:
- "content/**/*.md"
config:
MD001: true
MD003:
style: atx
MD004:
style: dash
MD005: true
MD007: false
MD009: true
MD010: true
MD011: true
MD012: true
MD013: false
MD014: true
MD018: true
MD019: true
MD020: true
MD021: true
MD022: true
MD023: true
MD024: false
MD025:
front_matter_title: "\\s*title\\s*="
level: 1
MD026: true
MD027: true
MD028: false
MD029:
style: one
MD030: true
MD031: true
MD032: true
MD033: false
MD034: false
MD035:
style: ---
MD036: true
MD037: true
MD038: true
MD039: true
MD040: true
MD041: false
MD042: true
MD043: false
MD044: false
MD045: true
MD046: false
MD047: true
MD048:
style: backtick
MD049:
style: underscore
MD050:
style: asterisk
MD051: false
MD052: true
MD053: true

View File

@ -1,56 +0,0 @@
MD001: true
MD003:
style: atx
MD004:
style: dash
MD005: true
MD007: false
MD009: true
MD010: true
MD011: true
MD012: true
MD013: false
MD014: true
MD018: true
MD019: true
MD020: true
MD021: true
MD022: true
MD023: true
MD024: false
MD025:
front_matter_title: "\\s*title\\s*="
level: 1
MD026: true
MD027: true
MD028: false
MD029:
style: one
MD030: true
MD031: true
MD032: true
MD033: false
MD034: false
MD035:
style: ---
MD036: true
MD037: true
MD038: true
MD039: true
MD040: true
MD041: false
MD042: true
MD043: false
MD044: false
MD045: true
MD046: false
MD047: true
MD048:
style: backtick
MD049:
style: underscore
MD050:
style: asterisk
MD051: false
MD052: true
MD053: true

View File

@ -1,39 +0,0 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-merge-conflict
- id: check-yaml
- id: check-toml
- id: check-json
- id: end-of-file-fixer
- id: mixed-line-ending
args: [--fix=auto]
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- id: check-added-large-files
args: [--maxkb=1024]
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.48.0
hooks:
- id: markdownlint
args: [--config, .markdownlintrc]
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: v10.0.0
hooks:
- id: cspell
args: [--no-summary, --no-progress, --config, .cspell.config.yaml]
additional_dependencies:
- "@cspell/dict-pt-br"
types: [markdown]
- repo: local
hooks:
- id: lychee
name: lychee
entry: lychee
args: [--config, .lychee.toml, --root-dir, static, --no-progress]
language: system
types: [markdown]

View File

@ -1,62 +0,0 @@
# AGENTS.md — Thanwer's Blog
## Project
Hugo blog (multilingual EN + PT-BR) using the **anubis2** theme as a git submodule (`themes/anubis2`, v1.6.0). Deployed via rsync to a VPS.
## Commands
```bash
hugo server # dev server at localhost:1313
hugo # build to ./public/ (no minify)
./deploy.sh # pre-commit → hugo --minify → rsync
pre-commit run --all-files # quality check (lint, spell, links)
```
## Quality toolchain
- **pre-commit** — runs on `./deploy.sh` or manually via `pre-commit run --all-files`. Includes: YAML/TOML/JSON check, EOF fixer, trailing whitespace, large file check, markdownlint, cspell (EN + PT-BR), lychee (link checker).
- **markdownlint** — config: `.markdownlintrc`. MD013 (line-length) disabled.
- **cspell** — config: `.cspell.config.yaml`. Adds `@cspell/dict-pt-br` via `additional_dependencies`. Custom word list for technical terms.
- **lychee** — config: `.lychee.toml`. Run locally with `--root-dir static` to resolve `/images/...` links. Excludes social media domains and `sshaudit.com`.
All quality tooling must pass before deploy. No separate test framework.
## Content conventions
- **Posts**: `content/posts/<slug>.md` (EN), `content/posts/<slug>.pt.md` (PT-BR)
- **Images**: `static/images/<filename>` — referenced as `/images/<filename>` in markdown
- **PT translations** set `canonical = "/posts/<en-slug>"` in frontmatter
- **`lastmod`** must use bare TOML datetime (no quotes): `lastmod = 2026-05-09T00:00:00Z`.
- **Archetype**: `hugo new posts/<slug>.md``archetypes/default.md`
- **Tags** only (no categories): `tags = ["GNU/Linux", "Tools"]`
## Configuration quirks
- Theme requires **all lowercase keys** under `params.anubis2`: `colortheme`, `enablecolorthemeswitcher`, `readnextposts`, `socialicons`, etc. See `themes/anubis2/PARAMS.md`.
- `[params.anubis2.services.umami]` keys are lowercase: `id`, `url`.
- Analytics inject only in production (`hugo.IsProduction`). Test locally: `hugo server --environment production`.
- Config uses modern Hugo keys: `locale` (not `languageCode`), `label` (not `languageName`).
- `enableGitInfo = true``.Lastmod` derives from git; template prefers `.Params.lastmod` when set.
## Frontmatter reference
```toml
date = 2024-10-26T16:59:23-03:00
lastmod = 2026-05-09T00:00:00Z
title = 'Post Title'
description = "One-line summary for SEO and RSS"
tags = ["tag1", "tag2"]
canonical = "/posts/original-post" # PT translations only
```
## Deprecated keys (avoid)
- `languageCode``locale`; `languageName``label`; `.Site.LanguageCode``.Site.Language.Locale`
## Theme overrides (`layouts/` shadows theme)
- `layouts/partials/head.html` — canonical from `Params.canonical`, Umami analytics (prod only)
- `layouts/partials/post-info.html` — prefers `.Params.lastmod`, shows "[Last modified]" when different from date
- `layouts/partials/footer-extra.html` — Ko-fi widget
- `layouts/robots.txt` — custom sitemap for both languages

View File

@ -1,13 +0,0 @@
+++
date = '2026-05-09T00:00:00Z'
title = 'About'
description = "About Pietro Arancibia \"Thanwer\""
+++
Hey, I'm **Pietro Arancibia** (a.k.a. **Thanwer**), a sysadmin and GNU/Linux enthusiast currently based in Germany.
I created this blog to share practical knowledge about server administration, security, and tools I use in my day-to-day work. Most posts come from real problems I've encountered — things I wished someone had told me earlier.
When I'm not breaking or fixing servers, I enjoy contributing to open-source projects and exploring new technologies.
Feel free to reach out via any of the social links on this site!

View File

@ -1,14 +0,0 @@
+++
date = '2026-05-09T00:00:00Z'
title = 'Sobre'
description = "Sobre Pietro Arancibia \"Thanwer\""
canonical = "/about"
+++
Olá, sou **Pietro Arancibia** (conhecido como **Thanwer**), um sysadmin e entusiasta de GNU/Linux atualmente baseado na Alemanha.
Criei este blog para compartilhar conhecimento prático sobre administração de servidores, segurança e ferramentas que uso no meu dia a dia. A maioria dos posts vem de problemas reais que encontrei - coisas que gostaria que alguém tivesse me dito antes.
Quando não estou quebrando ou consertando servidores, gosto de contribuir com projetos open-source e explorar novas tecnologias.
Sinta-se à vontade para entrar em contato através dos links sociais deste site!

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-23T20:43:44-03:00' date = '2024-10-23T20:43:44-03:00'
title = 'Securing Your Linux Server with Fail2Ban for SSH Protection' title = 'Securing Your Linux Server with Fail2Ban for SSH Protection'
description = "Learn how to configure Fail2ban SSH jails on GNU/Linux with custom ban times, multipliers, and trusted IPs to protect your server from brute-force attacks." description = "Thanwer's Blog Post about how to use Fail2ban"
tags = [ tags = [
"webserver", "webserver",
"GNU/Linux", "GNU/Linux",
@ -42,7 +42,9 @@ Next, we have the `jail.conf` file, which is where we configure our "jails" as t
Here we are going to make some changes, so first let's create a `jail.local` file with the following content: Here we are going to make some changes, so first let's create a `jail.local` file with the following content:
```text
```
[DEFAULT] [DEFAULT]
bantime.multipliers = 5 15 30 60 300 720 1440 2880 bantime.multipliers = 5 15 30 60 300 720 1440 2880
bantime = 86400 bantime = 86400
@ -64,7 +66,6 @@ Take your time to fine tune those parameters.
## Enabling and testing Fail2ban ## Enabling and testing Fail2ban
Enable and start the fail2ban service Enable and start the fail2ban service
```bash ```bash
systemctl enable --now fail2ban systemctl enable --now fail2ban
``` ```
@ -72,7 +73,6 @@ systemctl enable --now fail2ban
For SSH jails the test is very simple, you can test by simply failing to log on a couple times. For SSH jails the test is very simple, you can test by simply failing to log on a couple times.
You can check the status with the command: You can check the status with the command:
```bash ```bash
fail2ban-client status fail2ban-client status
fail2ban-client status sshd fail2ban-client status sshd
@ -82,11 +82,11 @@ fail2ban-client status sshd
As you can see, the SSH service on a cloud VPS is constantly being attacked, my server has almost 400 currently banned IPs. As you can see, the SSH service on a cloud VPS is constantly being attacked, my server has almost 400 currently banned IPs.
## More jails ## More jails!
Today I showed how to protect your GNU/Linux SSH service, but is is not over, you can use fail2ban to protect any service that you run! Today I showed how to protect your GNU/Linux SSH service, but is is not over, you can use fail2ban to protect any service that you run!
You can even integrate fail2ban protection with [Cloudflare](https://github.com/nesrual/fail2ban-cloudflare) if you use their services! You can even integrate fail2ban protection with [Cloudflare](https://github.com/nesrual/fail2ban-cloudflare!) if you use their services!
I suggest you to check the `/etc/fail2ban/filter.d` folder, which contains the default filters for popular services, like Apache, Asterisk, Mail server services, etc... I suggest you to check the `/etc/fail2ban/filter.d` folder, which contains the default filters for popular services, like Apache, Asterisk, Mail server services, etc...

View File

@ -1,20 +1,20 @@
+++ +++
date = '2024-10-23T20:43:44-03:00' date = '2024-10-23T20:43:44-03:00'
title = 'Melhorando a segurança do seu servidor com o Fail2ban' title = 'Melhorando a segurança do seu servidor com o Fail2ban'
description = "Aprenda a configurar jails SSH no Fail2ban em GNU/Linux com tempos de banimento customizados, multiplicadores e IPs confiáveis para proteger seu servidor contra ataques de força bruta." description = "Post no blog do Thanwer sobre como usar o Fail2ban"
tags = [ tags = [
"webserver", "webserver",
"GNU/Linux", "GNU/Linux",
"Basics Series", "Basics Series",
] ]
canonical = "/posts/fail2ban-basics" canonical = "posts/fail2ban-basics"
+++ +++
[Fail2ban](https://github.com/fail2ban/fail2ban) é um software que escaneia arquivos de logs como o `/var/log/auth.log` e bane IPs com muitas falhas de login. [Fail2ban](https://github.com/fail2ban/fail2ban) é um software que escaneia arquivos de logs como o `/var/log/auth.log` e bane IPs com muitas falhas de login.
Isso é apenas uma camada de segurança e deve ser utilizada em conjunto com outras ferramentas como o iptables e melhorias na configuração do SSH. Isso é apenas uma camada de segurança e deve ser utilizada em conjunto com outras ferramentas como o iptables e melhorias na configuração do SSH.
Eu sempre configuro uma jail para SSH usando o fail2ban em meus servidores GNU/Linux. Eu sempre configuro uma jail para SSH usando o fail2ban em meus servidoresGNU/Linux.
Hoje, vou mostrar como configurar uma jail simples para o SSH. Hoje, vou mostrar como configurar uma jail simples para o SSH.
@ -43,7 +43,7 @@ Depois, temos o arquivo `jail.conf` , que é onde configuramos as nossas "jails"
Aqui vamos fazer algumas alterações, primeiro vamos criar um arquivo `jail.local` com o seguinte conteúdo: Aqui vamos fazer algumas alterações, primeiro vamos criar um arquivo `jail.local` com o seguinte conteúdo:
```text ```
[DEFAULT] [DEFAULT]
bantime.multipliers = 5 15 30 60 300 720 1440 2880 bantime.multipliers = 5 15 30 60 300 720 1440 2880
bantime = 86400 bantime = 86400
@ -65,7 +65,6 @@ Invista seu tempo para estudar e melhorar esses parâmetros.
## Ativando e testando o fail2ban ## Ativando e testando o fail2ban
Ative e inicie o serviço fail2ban: Ative e inicie o serviço fail2ban:
```bash ```bash
systemctl enable --now fail2ban systemctl enable --now fail2ban
``` ```
@ -73,7 +72,6 @@ systemctl enable --now fail2ban
Para testar as jails SSH, basta tentar o login com a senha incorreta por algumas vezes. Para testar as jails SSH, basta tentar o login com a senha incorreta por algumas vezes.
Voce pode ver o status com os seguintes comandos: Voce pode ver o status com os seguintes comandos:
```bash ```bash
fail2ban-client status fail2ban-client status
fail2ban-client status sshd fail2ban-client status sshd
@ -83,11 +81,11 @@ fail2ban-client status sshd
Como voce pode ver, o serviço SSH em uma VPS em cloud é constantemente atacada, meu servidor já tem mais de 400 IPs banidos. Como voce pode ver, o serviço SSH em uma VPS em cloud é constantemente atacada, meu servidor já tem mais de 400 IPs banidos.
## Mais jails ## Mais jails!
Hoje eu mostrei como proteger o seu serviço SSH em uma maquina GNU/Linux, mas não acabou, voce pode usar o fail2ban para qualquer serviço! Hoje eu mostrei como proteger o seu serviço SSH em uma maquina GNU/Linux, mas não acabou, voce pode usar o fail2ban para qualquer serviço!
Voce pode até mesmo integrar a proteção do fail2ban com o [Cloudflare](https://github.com/nesrual/fail2ban-cloudflare) se voce usa os serviços deles! Voce pode até mesmo integrar a proteção do fail2ban com o [Cloudflare](https://github.com/nesrual/fail2ban-cloudflare!) se voce usa os serviços deles!
Eu sugiro que voce veja o diretório `/etc/fail2ban/filter.d`, que contem os filtros default para vários serviços populares, como Apache, Asterisk, serviços de servidor de e-mail, etc... Eu sugiro que voce veja o diretório `/etc/fail2ban/filter.d`, que contem os filtros default para vários serviços populares, como Apache, Asterisk, serviços de servidor de e-mail, etc...

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-19T23:00:41-03:00' date = '2024-10-19T23:00:41-03:00'
title = 'First Post' title = 'First Post'
description = "Starting a sysadmin blog with Hugo: why I chose a static site generator, how I deployed it on Hetzner Cloud via rsync, and what to expect from this blog." description = "A simple blog post about using Hugo as a framework for Thanwer's Blog"
tags = [ tags = [
"webserver", "webserver",
"Hugo", "Hugo",
@ -28,10 +28,9 @@ You can also use [Github Pages](https://pages.github.com/) or [Cloudflare Pages]
## The deploy ## The deploy
I followed Hugo guide to [deploy by rsync](https://gohugo.io/hosting-and-deployment/deployment-with-rsync/) and it worked very well for me. I followed Hugo guide to [deploy by rsync](https://gohugo.io/hosting-and-deployment/deployment-with-rsync/) and I worked very well for me.
A simple bash script to generate the website and deploy, all in one line! A simple bash script to generate the website and deploy, all in one line!
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
hugo && rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/ hugo && rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/
@ -39,4 +38,4 @@ hugo && rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/
## The Conclusion ## The Conclusion
From my experience until now (which is only the contents of this post btw...), this platform seems to meet my expectations I will continue to bring more topics and keep you guys posted! From my experience until now (which is only the contents of this post btw...), this platform seems to meet my expectations I will continue to bring move topics and keep you guys posted!

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-19T23:00:41-03:00' date = '2024-10-19T23:00:41-03:00'
title = 'First Post' title = 'First Post'
description = "Iniciando um blog de sysadmin com Hugo: por que escolhi um gerador de sites estáticos, como fiz deploy na Hetzner Cloud via rsync, e o que esperar deste blog." description = "A simple bolog post about using Hugo as a framework for Thanwer's Blog"
tags = [ tags = [
"webserver", "webserver",
"Hugo", "Hugo",
@ -32,7 +32,6 @@ Você também pode usar o [Github Pages](https://pages.github.com/) ou o [Cloudf
Eu segui o guia oficial do Hugo para fazer o [deploy via rsync](https://gohugo.io/hosting-and-deployment/deployment-with-rsync/) e ele funcionou bem para mim Eu segui o guia oficial do Hugo para fazer o [deploy via rsync](https://gohugo.io/hosting-and-deployment/deployment-with-rsync/) e ele funcionou bem para mim
Um simples script em bash gera o site e faz o deploy, tudo em uma linha! Um simples script em bash gera o site e faz o deploy, tudo em uma linha!
```bash ```bash
#!/usr/bin/env bash #!/usr/bin/env bash
hugo && rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/ hugo && rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/
@ -40,4 +39,4 @@ hugo && rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/
## Conclusões ## Conclusões
Pela minha experiência até agora (que foi apenas o conteúdo dessa página a propósito...), essa plataforma parece atender minhas expectativas, vou continuar trazendo mais conteúdo e mantenho vocês informados. Pela minha experiência até agora (que foi apenas o conteúdo dessa pagina a propósito...), essa plataforma parece atender minhas expectativas, vou continuar trazendo mais conteúdo e mantenho vocês informados.

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-20T14:22:33-03:00' date = '2024-10-20T14:22:33-03:00'
title = 'Mastering iptables: Basic Firewall Configurations for a Newly Deployed GNU/Linux Server' title = 'Mastering iptables: Basic Firewall Configurations for a Newly Deployed GNU/Linux Server'
description = "A step-by-step guide to configuring iptables firewall rules for a newly deployed GNU/Linux server, covering SSH, HTTP/HTTPS, IPv6, ICMP, and security best practices." description = "The basic usage of iptables for a new GNU/Linux webserver"
tags = [ tags = [
"webserver", "webserver",
"GNU/Linux", "GNU/Linux",
@ -34,14 +34,12 @@ You can check those well made diagrams by [nerdalert on Github](https://gist.git
Lets start with a quick overview of essential iptables commands: Lets start with a quick overview of essential iptables commands:
### Show current rules ### Show current rules:
```bash ```bash
iptables -L iptables -L
``` ```
### Flush (delete) all existing rules ### Flush (delete) all existing rules:
```bash ```bash
iptables -F iptables -F
``` ```
@ -89,7 +87,6 @@ iptables -P OUTPUT ACCEPT
``` ```
This means any incoming connection will be dropped unless you explicitly allow it. Outgoing traffic, on the other hand, is allowed by default. This means any incoming connection will be dropped unless you explicitly allow it. Outgoing traffic, on the other hand, is allowed by default.
### Step 3: Allow SSH Traffic (Port 22) ### Step 3: Allow SSH Traffic (Port 22)
Youll need SSH access to manage your server, so well allow traffic on port 22. Its important to only allow connections from trusted IP addresses, but for simplicity, well start by allowing all connections on SSH: Youll need SSH access to manage your server, so well allow traffic on port 22. Its important to only allow connections from trusted IP addresses, but for simplicity, well start by allowing all connections on SSH:
@ -154,7 +151,8 @@ iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
This ensures your server can accept responses to any requests it makes. This ensures your server can accept responses to any requests it makes.
### Step 8: Don't forget IPv6 rules
### Step 8: Don't forget IPv6 rules!
For IPv6, the syntax is the same, you mostly need to change the command from `iptables` to `ip6tables` For IPv6, the syntax is the same, you mostly need to change the command from `iptables` to `ip6tables`

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-20T14:22:33-03:00' date = '2024-10-20T14:22:33-03:00'
title = 'Dominando o iptables: Configurações básicas de firewall para um servidor GNU/Linux recém-implantado' title = 'Dominando o iptables: Configurações básicas de firewall para um servidor GNU/Linux recém-implantado'
description = "Um guia passo a passo para configurar regras de firewall iptables em um servidor GNU/Linux recém-implantado, cobrindo SSH, HTTP/HTTPS, IPv6, ICMP e melhores práticas de segurança." description = "O uso básico do iptables para um novo servidor web GNU/Linux"
tags = [ tags = [
"webserver", "webserver",
"GNU/Linux", "GNU/Linux",
@ -14,7 +14,7 @@ Ao configurar um novo servidor Linux, uma das primeiras coisas que você deve fa
Nesta publicação, vou mostrar algumas configurações básicas do iptables para um servidor recém-implantado. No final, você terá um firewall sólido para proteger seus serviços e, ao mesmo tempo, manter as coisas simples. Nesta publicação, vou mostrar algumas configurações básicas do iptables para um servidor recém-implantado. No final, você terá um firewall sólido para proteger seus serviços e, ao mesmo tempo, manter as coisas simples.
## O que é iptables? ## Oque é iptables?
iptables é um utilitário de firewall por linha de comando que utiliza "chains" para gerenciar o tráfego. As "chains" são basicamente uma lista de regras que aplicam para pacotes de entrada ou saída. Voce pode fazer as regras baseadas em IPs de origem, portas e protocolos, por exemplo. iptables é um utilitário de firewall por linha de comando que utiliza "chains" para gerenciar o tráfego. As "chains" são basicamente uma lista de regras que aplicam para pacotes de entrada ou saída. Voce pode fazer as regras baseadas em IPs de origem, portas e protocolos, por exemplo.
@ -36,13 +36,11 @@ Recomendo que voce inicie por esses diagramas muito bem feitos nesse gist do [ne
Vamos começar com uma overview dos comandos essenciais do iptables: Vamos começar com uma overview dos comandos essenciais do iptables:
### Mostrar regras atuais ### Mostrar regras atuais
```bash ```bash
iptables -L iptables -L
``` ```
### Deletar regras atuais ### Deletar regras atuais
```bash ```bash
iptables -F iptables -F
``` ```
@ -155,7 +153,7 @@ iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Isso garante que o servidor aceite respostas para requisições que ele mesmo fez. Isso garante que o servidor aceite respostas para requisições que ele mesmo fez.
### Passo 8: Não esqueça o IPv6 ### Passo 8: Não esqueça o IPv6!
Para IPv6, a sintaxe é a mesma, voce apenas precisa mudar o comando de `iptables` para `ip6tables` Para IPv6, a sintaxe é a mesma, voce apenas precisa mudar o comando de `iptables` para `ip6tables`

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-31T22:00:53-03:00' date = '2024-10-31T22:00:53-03:00'
title = 'Netcat' title = 'Netcat'
description = "Learn how to use netcat (nc) for network troubleshooting, file transfers, port scanning, and directly communicating with services like SMTP on GNU/Linux." description = "Thanwer's Blog Post about netcat tool"
tags = [ tags = [
"GNU/Linux", "GNU/Linux",
"Tools", "Tools",
@ -12,8 +12,8 @@ I heard too many times a work colleague, or a third-party vendor telling me "the
You can use websites to try to test for open ports, a quick Google search should yield you a couple good results, but sometimes that is not enough. You can use websites to try to test for open ports, a quick Google search should yield you a couple good results, but sometimes that is not enough.
## The problem
## The problem
Let's suppose you followed my other post about [restricting source address with iptables]({{< ref "/posts/iptables-basics" >}} "Iptables basics"), in this case those random web tools won't help you, so you use nmap! Let's suppose you followed my other post about [restricting source address with iptables]({{< ref "/posts/iptables-basics" >}} "Iptables basics"), in this case those random web tools won't help you, so you use nmap!
I will make a post about nmap in the future, for now let's use it in a very simple way to check for an open port: I will make a post about nmap in the future, for now let's use it in a very simple way to check for an open port:
@ -47,9 +47,8 @@ Netcat is a very simple, but very powerful tool for those cases. With that, you
### Installing ### Installing
The installation is very simple! On Debian systems you can do it with the command: The installation is very simple! On Debian systems you can do it with the command:
```bash ```bash
sudo apt install netcat $ sudo apt install netcat
``` ```
### Using the tool ### Using the tool
@ -58,27 +57,24 @@ I invite you to read the [nc man page](https://linux.die.net/man/1/nc).
The main options we need to use are: The main options we need to use are:
- -p : This specifies which port should we listen to. * -p : This specifies which port should we listen to.
- -k : This makes netcat to keep listening after a client disconnects. * -k : This makes netcat to keep listening after a client disconnects.
- -u : Use UDP instead of TCP * -u : Use UDP instead of TCP
### Use cases ### Use cases
#### Check for network communication #### Check for network communication
Listen on port 2000: Listen on port 2000:
```bash ```bash
nc -l 2000 $ nc -l 2000
``` ```
This starts a server which listens on port 2000. This starts a server which listens on port 2000.
Now let's open another terminal and connect a client to this port: Now let's open another terminal and connect a client to this port:
```bash ```bash
nc localhost 2000 $ nc localhost 2000
``` ```
@ -95,13 +91,12 @@ Netcat can be used to send and receive files between two hosts over a network. T
On the receivers end: On the receivers end:
```bash ```bash
nc -l -p 1234 > received_file.txt $ nc -l -p 1234 > received_file.txt
``` ```
On the senders end, assuming that 192.0.0.2 is the receiving server: On the senders end, assuming that 192.0.0.2 is the receiving server:
```bash ```bash
nc 192.0.0.2 1234 < file_to_send.txt $ nc 192.0.0.2 -p 1234 < file_to_send.txt
``` ```
This can be useful for transferring a log file for example. This can be useful for transferring a log file for example.
@ -109,7 +104,6 @@ This can be useful for transferring a log file for example.
#### Port scan #### Port scan
nc can be used as a simpler alternative to nmap: nc can be used as a simpler alternative to nmap:
```bash ```bash
$ nc -z host.example.com 20-30 $ nc -z host.example.com 20-30
Connection to host.example.com 22 port [tcp/ssh] succeeded! Connection to host.example.com 22 port [tcp/ssh] succeeded!

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-31T22:00:53-03:00' date = '2024-10-31T22:00:53-03:00'
title = 'Netcat' title = 'Netcat'
description = "Aprenda a usar o netcat (nc) para troubleshooting de rede, transferência de arquivos, varredura de portas e comunicação direta com serviços como SMTP no GNU/Linux." description = "Post no blog do Thanwer sobre a ferramenta netcat."
tags = [ tags = [
"GNU/Linux", "GNU/Linux",
"Tools", "Tools",
@ -14,7 +14,6 @@ Já ouvi muitas vezes de algum colega de trabalho, ou de algum prestador de serv
Voce pode usar testes online para verificar as portas abertas, uma rápida pesquisa no Google vai te trazer resultados bons, porem nem sempre isso é o suficiente. Voce pode usar testes online para verificar as portas abertas, uma rápida pesquisa no Google vai te trazer resultados bons, porem nem sempre isso é o suficiente.
## O problema ## O problema
Vamos supor que voce seguiu meu outro post sobre [restringir por endereço de origem no iptables]({{< ref "/posts/iptables-basics" >}} "Iptables basics"), nesse caso essas ferramentas aleatórias não vão lhe ajudar, então voce pode usar o nmap! Vamos supor que voce seguiu meu outro post sobre [restringir por endereço de origem no iptables]({{< ref "/posts/iptables-basics" >}} "Iptables basics"), nesse caso essas ferramentas aleatórias não vão lhe ajudar, então voce pode usar o nmap!
Farei um post sobre o nmap no futuro, por enquanto vamos usa-lo de forma bem simples para verificar se uma porta está aberta: Farei um post sobre o nmap no futuro, por enquanto vamos usa-lo de forma bem simples para verificar se uma porta está aberta:
@ -48,9 +47,8 @@ O netcat é uma ferramenta super simples, porem muito poderosa para esses casos.
### Instalação ### Instalação
A instalação é muito simples! Em um sistema Debian basta executar o seguinte comando: A instalação é muito simples! Em um sistema Debian basta executar o seguinte comando:
```bash ```bash
sudo apt install netcat $ sudo apt install netcat
``` ```
### Usando a ferramenta ### Usando a ferramenta
@ -59,27 +57,24 @@ Lhes convido a ler a [man page do nc](https://linux.die.net/man/1/nc).
Os principais argumentos que precisamos são: Os principais argumentos que precisamos são:
- -p : Isso especifica a porta que queremos escutar. * -p : Isso especifica a porta que queremos escutar.
- -k : Isso mantém o netcat aberto mesmo depois do cliente desconectar. * -k : Isso mantem o netcat aberto mesmo depois do cliente desconectar.
- -u : Isso faz o netcat escutar UDP em vez de TCP. * -u : Isso faz o netcat escutar UDP em vez de TCP.
### Casos de uso ### Casos de uso
#### Testar comunicação de rede #### Testar comunicação de rede
Escutar na porta 2000: Escutar na porta 2000:
```bash ```bash
nc -l 2000 $ nc -l 2000
``` ```
Isso inicia um serviço na porta 2000. Isso inicia um serviço na porta 2000.
Agora vamos abrir outro terminal e conectar um cliente a essa porta: Agora vamos abrir outro terminal e conectar um cliente a essa porta:
```bash ```bash
nc localhost 2000 $ nc localhost 2000
``` ```
@ -89,28 +84,26 @@ Veja um exemplo disso funcionando:
![Exemplo de uso do netcat](/images/nc-example1.gif) ![Exemplo de uso do netcat](/images/nc-example1.gif)
#### Transferência de arquivos #### Transferencia de arquivos
O netcat também pode ser usado para transferir arquivos entre dois hosts, isso é util em casos de ambientes sem acessos FTP ou SCP. O netcat tambem pode ser usado para transferir arquivos entre dois hosts, isso é util em casos de ambientes sem acessos FTP ou SCP.
No receptor: No receptor:
```bash ```bash
nc -l -p 1234 > arquivo_recebido.txt $ nc -l -p 1234 > arquivo_recebido.txt
``` ```
No transmissor, assumindo que o receptor seja o IP 192.0.0.2:: No transmissor, assumindo que o receptor seja o IP 192.0.0.2::
```bash ```bash
nc 192.0.0.2 1234 < arquivo_para_enviar.txt $ nc 192.0.0.2 -p 1234 < arquivo_para_enviar.txt
``` ```
Isso pode ser util para transferir um arquivo de log, por exemplo. Isso pode ser util para transferir um arquivo de log, por exemplo.
#### Port scan #### Port scac
nc pode ser usado como uma alternativa "mais barata" para o nmap: nc pode ser usado como uma alternativa "mais barata" para o nmap:
```bash ```bash
$ nc -z host.example.com 20-30 $ nc -z host.example.com 20-30
Connection to host.example.com 22 port [tcp/ssh] succeeded! Connection to host.example.com 22 port [tcp/ssh] succeeded!

View File

@ -1,82 +0,0 @@
+++
date = '2025-10-12T13:41:47+02:00'
title = 'Shrink VMDK safely'
description = "A safe method to shrink VMDK disk files in ESXi using Clonezilla, without manually editing VMX files — perfect for physical-to-virtual conversions."
tags = [
"GNU/Linux",
"Tools",
"ESXi",
]
+++
I recently was tasked to convert a couple physical servers to virtual machines.
The physical servers basically had 2x 500GB SSDs in a RAID1 configuration, and the total used space was around 40GB.
To optimize the storage for the virtual machines, I needed to resize the VMDK files accordingly. This process involves several steps to ensure data integrity and minimize downtime.
Here I document the steps I took to safely resize a VMDK file, without modifying the virtual machine description file (VMX).
## Step 1: Prepare the physical server
Before starting, I did the basic preparation on the physical server:
- As always, create a full backup of the server, just in case something goes wrong
- Clean up unnecessary files to reduce the used space
- Resize the partitions and filesystems to reduce the used space to the smallest possible size (in my case, I resized the partitions to 64GB to have some buffer space)
![Gparted result](/images/vmdk-gparted-1.jpg)
## Step 2: Convert the physical server to a virtual machine
I used Clonezilla to convert the physical server to a virtual machine.
This process was simply:
- Boot the physical server with the Clonezilla live CD
- Follow the wizard and choose the mode "remote-source" to send the image to a remote server
- On the target virtual machine, boot with the Clonezilla live CD and choose the mode "remote-dest" to receive the image from the physical server
- Inform the IP address of the physical server and start the process
> Important: In this first step, I created a VMDK file with the same size as the physical disk (in my case, 500GB).
![ESXi Disks](/images/vmdk-disks.jpg)
## Step 3: Resize the VMDK file
Now here is the tricky part, resizing the VMDK file without modifying the VMX file.
Many tutorials suggest to access the ESXi host via SSH and directly modify the VMX file using a text editor.
I found it to be very risky, as a small mistake can lead to a non-bootable virtual machine. I lost many attempts this way.
So instead, I found a method that works without modifying the VMX file:
Use Clonezilla again, to clone the virtual machine to a new virtual machine with a smaller disk size.
This time we are going to use local disks, so we need to create a new virtual machine with a smaller disk size (in my case, 64GB).
![Clonezilla local source](/images/clonezilla-1.jpg)
When asked about the partition table, choose the option "-k1" to keep the partition table as is.
![Clonezilla partition table](/images/clonezilla-2.jpg)
When asked about the mode, choose "Expert"
![Clonezilla expert mode](/images/clonezilla-3.jpg)
Then, enable the option "-icds" to ignore the disk size.
![Clonezilla ignore disk size](/images/clonezilla-4.jpg)
After following the rest of the wizard, the cloning process will start.
![Clonezilla cloning](/images/clonezilla-5.jpg)
After the process is complete, you will have a new virtual machine with a smaller disk size, and the same data as the original virtual machine.
This will leave you with the original VMDK file, and a smaller one. After verifying that the new virtual machine works correctly, you can delete the original one to free up space.
## Conclusion
Using Clonezilla to clone to a smaller disk is a safe and reliable method to shrink VMDK files without touching VMX files directly. While it requires more steps than the manual approach, it reduces the risk of configuration errors that could leave your VM unbootable. As always, keep backups before attempting any disk resizing operations.

View File

@ -1,150 +0,0 @@
+++
date = '2025-09-14T10:19:39+02:00'
title = 'Slurm'
description = "A practical guide to setting up a Slurm cluster for HPC and bioinformatics workloads, covering installation, configuration, job submission, and useful tips."
tags = [
"GNU/Linux",
"Tools",
]
+++
So recently I was tasked to set up a Slurm cluster for bioinformatics workloads, and I thought it would be a good idea to share some notes about the installation and configuration process.
So in short, before we start, what is Slurm?
> Slurm is an open-source, fault-tolerant, and highly scalable cluster management and job scheduling system for large and small Linux clusters. It is used by many of the world's supercomputers and computer clusters.
Before we have Slurm, the bioinformaticians used to access the servers directly via SSH, open a screen/tmux session, and run their jobs directly on the servers. This approach has several drawbacks:
- Resource management: Without a job scheduler, it is difficult to manage and allocate resources efficiently. Suppose you have a server with 128Gb of RAM, and your job requires 32Gb of RAM to run, so you think "I can run 4 jobs in parallel", but if another user is running a job that requires 64Gb of RAM, your job will fail due to insufficient resources.
- Job prioritization: Without a job scheduler, it is difficult to prioritize jobs based on their importance or urgency. This can lead to situations where less important jobs are running while more important jobs are waiting in the queue.
- Queue management: Without a job scheduler, the users have to manually monitor and send new jobs when the previous one finishes. Now imagine that your job takes 16 hours to run, will you SSH into the server at 2AM to send a new job? Probably not, so your jobs will be delayed, and the server will just sit idle while you sleep. This is inefficient.
With that in mind, it is very clear that we need a job scheduler, and Slurm is a great choice for that.
## Slurm operation
### The main workflow
The main workflow of Slurm is quite simple:
1. The user creates a special bash script with the job parameters and the commands to run. Here is an example of such a script:
```bash
#!/usr/bin/env bash
#SBATCH --job-name=my_job_name # Job name
#SBATCH --output=output.txt # Standard output file
#SBATCH --error=error.txt # Standard error file
#SBATCH --partition=partition_name # Partition or queue name
#SBATCH --nodes=1 # Number of nodes
#SBATCH --ntasks-per-node=1 # Number of tasks per node
#SBATCH --cpus-per-task=1 # Number of CPU cores per task
#SBATCH --time=1:00:00 # Maximum runtime (D-HH:MM:SS)
#SBATCH --mail-type=END # Send email at job completion
#SBATCH --mail-user=your@email.com # Email address for notifications
# Your commands go here
module load python/3.8
python my_script.py
```
1. The user submits a job to the Slurm controller using the `sbatch` command
1. The Slurm controller adds the job to the queue and assigns it a job ID
1. The Slurm controller schedules the job to run on a compute node when resources are available
1. The compute node executes the job and sends the output back to the user
The user can monitor the job status using the `squeue` command.
The administrator can manage the jobs using the `scancel` command to cancel a job, or the `scontrol` command to modify a job.
### Basic commands
Check the cluster status:
```bash
sinfo
PARTITION AVAIL TIMELIMIT NODES STATE NODELIST
debug up 30:00 2 idle node[01-02]
batch up 2-00:00:00 4 alloc node[03-06]
```
List running and pending jobs:
```bash
squeue
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON)
1001 batch test.sh alice R 0:15 2 node[03-04]
1002 debug script bob PD 0:00 1 (Priority)
```
Run a interactive job session:
```bash
srun --partition=debug --nodes=1 --ntasks=1 --time=00:30:00 --pty bash
```
Cancel a job:
```bash
scancel <job_id>
```
## Slurm components
The main components of Slurm are:
- **slurmctld**: The Slurm controller daemon, which is responsible for managing the cluster and scheduling jobs. It runs on the controller node.
- **slurmd**: The Slurm daemon, which is responsible for executing jobs on the compute nodes. It runs on each compute node.
- **slurmdbd**: The Slurm database daemon, which is responsible for storing job and user information in a database. It is optional and can run on a separate node.
## Installation
The installation process is quite straightforward, and you can find the [official documentation here](https://slurm.schedmd.com/quickstart_admin.html).
In my case, I used a Debian 13 VM as a all-in-one controller and multiple physical Debian 13 servers as compute nodes. In a more robust setup, you would probably want to have a separate database server, multiple controllers for redundancy, but for this example, since the cluster is relatively small, we will use the simplest configuration.
Since the configuration is the same for all nodes, this is a perfect scenario to be automated by [Ansible](https://www.ansible.com/), I will not cover the Ansible part here, but I will show you the main gotchas that I encountered and would save a lot of time.
## Tips and tricks
### Find real user documentation
Since Slurm is very popular in the academic world, there are many universities that have their own documentation, it is a very good starting point to find real user documentation, here are some examples that were very helpful to me:
[Advanced Research Computing at Hopkins](https://docs.arch.jhu.edu/en/latest/)
[Freie Universität Berlin](https://wiki.physik.fu-berlin.de/it/services:cluster:slurm)
[HPC Wiki](https://hpc-wiki.info/hpc/HPC_Wiki)
### Configless mode
Since Slurm 20.02, it supports a configless mode, which means that you don't need to create a `/etc/slurm/slurm.conf` file on each node, instead, you can use the `slurmctld` to distribute the configuration to all nodes.
This is a great feature, and it simplifies the installation process a lot. [The official documentation is here](https://slurm.schedmd.com/configless_slurm.html).
It just depends on a very simple DNS SRV record, so make sure you have that set up correctly. Take this example, and adapt it to your setup:
```bind
_slurmctld._tcp 3600 IN SRV 10 0 6817 slurmctl-backup
_slurmctld._tcp 3600 IN SRV 0 0 6817 slurmctl-primary
```
### Slurm-web
Slurm-web is a web interface for Slurm, which allows you (and your users!) to monitor and manage the cluster from a web browser. You can find the [official documentation here](https://slurm-web.com/).
![Slurm-web dashboard](/images/slurm-web.webp)
### slurm-mail
Slurm-Mail is a drop in replacement for Slurm's e-mails to give users much more information about their jobs compared to the standard Slurm e-mails. You can find it [on Github](https://github.com/neilmunday/slurm-mail).
### Power saving
Now this is a feature that I was not aware of, but Slurm has a built-in power saving feature, which allows you to automatically power on/off compute nodes based on demand.
It relies on scripts that you need to provide, one for powering on a node, and another for powering off a node. You can find the [Slurm power saving documentation](https://slurm.schedmd.com/power_save.html).
In my case I adapted [the scripts from here](https://github.com/OleHolmNielsen/Slurm_tools/tree/master/power_save) to use IPMI to power on/off the nodes.

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-26T16:59:23-03:00' date = '2024-10-26T16:59:23-03:00'
title = 'Advanced SSH Hardening Best Practices' title = 'Advanced SSH Hardening Best Practices'
description = "Strengthen your SSH server security with key-based authentication, 2FA, encryption hardening, access restrictions, and disabling unused auth methods." description = "Thanwer's Blog Post about best practices for hardening SSH servers"
tags = [ tags = [
"webserver", "webserver",
"GNU/Linux", "GNU/Linux",
@ -55,7 +55,6 @@ When you finish those tunings, check with the [ssh-audit tool](https://github.co
On top of iptables rules, the `/etc/ssh/sshd_config` file can apply restrictions based on users, groups and/or source IPs. On top of iptables rules, the `/etc/ssh/sshd_config` file can apply restrictions based on users, groups and/or source IPs.
For example, to whitelist by IP, you can add the following: For example, to whitelist by IP, you can add the following:
```bash ```bash
# Restrict to a specific IP address # Restrict to a specific IP address
AllowUsers *@192.0.0.2 AllowUsers *@192.0.0.2
@ -65,7 +64,3 @@ AllowUsers *@192.0.0.0/24
Match User alice Match User alice
AllowUsers alice@192.0.0.2 AllowUsers alice@192.0.0.2
``` ```
## Conclusion
SSH hardening is a continuous process, not a one-time task. Start with key-based authentication and disable password login, then layer on 2FA, encryption hardening, and access restrictions. Combined with iptables rules and fail2ban jails, you can significantly reduce the attack surface of your server. Review your SSH configuration periodically and keep up with the latest security recommendations.

View File

@ -1,7 +1,7 @@
+++ +++
date = '2024-10-26T16:59:23-03:00' date = '2024-10-26T16:59:23-03:00'
title = 'Boas práticas para configuração do SSH' title = 'Boas praticas para configuração do SSH'
description = "Fortaleça a segurança do seu servidor SSH com autenticação por chaves, 2FA, hardening de criptografia, restrições de acesso e desativação de métodos não utilizados." description = "Post no blog do Thanwer sobre melhores praticas para configurar servidores SSH"
tags = [ tags = [
"webserver", "webserver",
"GNU/Linux", "GNU/Linux",
@ -14,7 +14,7 @@ Como voce pode ver no meu ultimo post sobre [fail2ban]({{< ref "/posts/fail2ban-
Além de regras do iptables e jails do fail2ban, temos alguns parâmetros que podemos ajustar nos nossos servidores para termos mais uma medida de proteção. Além de regras do iptables e jails do fail2ban, temos alguns parâmetros que podemos ajustar nos nossos servidores para termos mais uma medida de proteção.
## Métodos de autenticação SSH ## Métodos de autenticação SSG
Por padrão, o serviço SSH permite autenticação por senha para todos os usuários exceto o root, oque é um bom começo, mas podemos fazer mais. Por padrão, o serviço SSH permite autenticação por senha para todos os usuários exceto o root, oque é um bom começo, mas podemos fazer mais.
@ -33,7 +33,7 @@ PermitRootLogin no
### Autenticação por dois fatores (2FA) ### Autenticação por dois fatores (2FA)
Se voce quiser mais um nível de segurança, voce pode implementar [autenticação de dois fatores com o Google Authenticator](https://docs.vultr.com/how-to-use-two-factor-authentication-with-sudo-and-ssh-on-linux-with-google-authenticator). Se voce quiser mais um nivel de segurança, voce pode implementar [autenticação de dois fatores com o Google Autenticator](https://docs.vultr.com/how-to-use-two-factor-authentication-with-sudo-and-ssh-on-linux-with-google-authenticator).
### Desativar métodos de autenticação não utilizados ### Desativar métodos de autenticação não utilizados
@ -47,7 +47,7 @@ GSSAPIAuthentication no
## Melhorar algoritmos de criptografia do SSH ## Melhorar algoritmos de criptografia do SSH
Seguindo [esses guias](https://www.sshaudit.com/hardening_guides.html) voce pode desabilitar algoritmos de autenticação e criptografia vulneráveis e priorizar os mais fortes (AES-256-GCM, chacha20-poly1305). Seguindo [esses guias](https://www.sshaudit.com/hardening_guides.html) voce pode desabilitar algoritmos de autenticação e criptografia vulneraveis e priorizar os mais fortes (AES-256-GCM, chacha20-poly1305).
Quando voce terminar, verifique com a ferramenta [ssh-audit](https://github.com/jtesta/ssh-audit). Quando voce terminar, verifique com a ferramenta [ssh-audit](https://github.com/jtesta/ssh-audit).
@ -56,7 +56,6 @@ Quando voce terminar, verifique com a ferramenta [ssh-audit](https://github.com/
Além das regras do iptables, o arquivo `/etc/ssh/sshd_config` pode aplicar restrições baseadas em usuário, grupo e/ou IP de origem. Além das regras do iptables, o arquivo `/etc/ssh/sshd_config` pode aplicar restrições baseadas em usuário, grupo e/ou IP de origem.
Por exemplo, para permitir conexões por IP, voce pode adicionar conforme os seguintes exemplos: Por exemplo, para permitir conexões por IP, voce pode adicionar conforme os seguintes exemplos:
```bash ```bash
# Restrict to a specific IP address # Restrict to a specific IP address
AllowUsers *@192.0.0.2 AllowUsers *@192.0.0.2
@ -66,7 +65,3 @@ AllowUsers *@192.0.0.0/24
Match User alice Match User alice
AllowUsers alice@192.0.0.2 AllowUsers alice@192.0.0.2
``` ```
## Conclusão
O hardening do SSH é um processo contínuo, não uma tarefa única. Comece com autenticação por chaves e desative o login por senha, depois adicione camadas como 2FA, hardening de criptografia e restrições de acesso. Combinado com regras de iptables e jails do fail2ban, você pode reduzir significativamente a superfície de ataque do seu servidor. Revise sua configuração SSH periodicamente e mantenha-se atualizado com as recomendações de segurança mais recentes.

View File

@ -1,16 +1,2 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail hugo && rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/
echo "=== Running pre-commit hooks ==="
pre-commit run --all-files
echo ""
echo "=== Building site ==="
hugo --minify
echo ""
echo "=== Deploying to server ==="
rsync -avz --delete public/ shinobi.thanwer.com:/srv/www/blog/
echo ""
echo "=== Done ==="

View File

@ -1,87 +1,28 @@
baseURL = 'https://blog.thanwer.com/' baseURL = 'https://blog.thanwer.com/'
locale = 'en-US' languageCode = 'en-us'
theme = "anubis2" theme = "anubis2"
title = "Thanwer's Blog" title = "Thanwer's Blog"
enableRobotsTXT = true enableRobotsTXT = true
enableGitInfo = true enableGitInfo = true
defaultContentLanguage = 'en' defaultContentLanguage = 'en'
[pagination]
pagerSize = 5
[taxonomies]
tag = "tags"
[languages] [languages]
[languages.en] [languages.en]
label = 'EN' languageName = 'EN'
weight = 1 weight = 1
[[languages.en.menu.main]]
identifier = "home"
name = "Home"
url = "/"
weight = 1
[[languages.en.menu.main]]
identifier = "tags"
name = "Tags"
url = "/tags/"
weight = 2
[[languages.en.menu.main]]
identifier = "archive"
name = "Archive"
url = "/posts/"
weight = 3
[[languages.en.menu.main]]
identifier = "about"
name = "About"
url = "/about/"
weight = 4
[languages.pt] [languages.pt]
label = 'BR' languageName = 'BR'
locale = 'pt-BR' languageCode = 'pt-BR'
weight = 2 weight = 2
[[languages.pt.menu.main]]
identifier = "home"
name = "Início"
url = "/"
weight = 1
[[languages.pt.menu.main]]
identifier = "tags"
name = "Tags"
url = "/tags/"
weight = 2
[[languages.pt.menu.main]]
identifier = "archive"
name = "Arquivo"
url = "/posts/"
weight = 3
[[languages.pt.menu.main]]
identifier = "about"
name = "Sobre"
url = "/about/"
weight = 4
[params] [params]
author.name = "Pietro Arancibia" author.name = "Pietro Arancibia"
author.email = "admin@thanwer.com" author.email = "admin@thanwer.com"
author.location = "Germany" author.location = "Germany"
description = "A blog written by Pietro Arancibia \"Thanwer\""
dateFormat = "2006-01-02"
[params.anubis2]
colorTheme = "auto" colorTheme = "auto"
enableColorThemeSwitcher = true colorThemeSwitcher = true
readNextPosts = 3 description = "A blog written by Pietro Arancibia \"Thanwer\""
socialIcons = [ social = [
{ id = "github", url = "https://github.com/thanwer" }, { id = "github", url = "https://github.com/thanwer" },
{ id = "linkedin", url = "https://www.linkedin.com/in/thanwer/" }, { id = "linkedin", url = "https://www.linkedin.com/in/thanwer/" },
{ id = "email", url = "mailto:admin@thanwer.com" }, { id = "email", url = "mailto:admin@thanwer.com" },
@ -89,15 +30,10 @@ socialIcons = [
{ id = "instagram", url = "https://www.instagram.com/thanwer/" }, { id = "instagram", url = "https://www.instagram.com/thanwer/" },
{ id = "telegram", url = "https://t.me/thanwer" }, { id = "telegram", url = "https://t.me/thanwer" },
{ id = "x", url = "https://x.com/thanwer" }, { id = "x", url = "https://x.com/thanwer" },
{ id = "youtube", url = "https://youtube.com/lordthanwer" }, { id = "youtube", url = "https://youtube.com/lordthanwer" }
] ]
[params.anubis2.services.umami]
id = "eded536d-a598-471a-945b-faaebc4837a9"
url = "https://umami.thanwer.com/script.js"
[markup.goldmark.renderer] [markup.goldmark.renderer]
unsafe = true unsafe = true # true | false; Enable unsafe mode to have a better experience
[markup.highlight] [markup.highlight]
style = 'base16-snazzy' style = 'base16-snazzy' # Highlight.js style

View File

@ -1,33 +0,0 @@
<!DOCTYPE html>
{{/* Cache params once for all partials */}}
{{ $params := partial "params-helper.html" . }}
{{ $dataTheme := "" }}
{{ if and (ne $params.colorTheme "auto") (eq $params.enableColorThemeSwitcher false) }}
{{ $dataTheme = $params.colorTheme }}
{{ end }}
<html lang="{{ site.Params.locale | default site.Language.Locale }}" data-theme="{{ $dataTheme }}">
<head>
{{ block "head" . }}
{{ partial "head/head.html" . }}
{{ end }}
</head>
<body>
<a class="skip-main" href="#main">{{ i18n "skipToContent" }}</a>
<div class="container">
<header class="common-header">
{{ block "header" . }}
{{ partial "head/header.html" . }}
{{ end }}
</header>
<main id="main" tabindex="-1">
{{ block "main" . }}{{ end }}
</main>
{{ block "footer" . }}
{{ partial "footer.html" . }}
{{ end }}
</div>
</body>
</html>

View File

@ -1,59 +0,0 @@
{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = site }}{{- end -}}
{{- $pages := where (where $pctx.RegularPages ".Params.disable_feed" "!=" true) "Params.private" "!=" true -}}
{{- $limit := site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
<channel>
<title>{{ with .Site.Params.feedTitle }}{{ . }}{{ else }}{{ .Site.Title }}{{ end }}</title>
<link>{{ .Site.BaseURL }}</link>
<description>Recent blog posts on {{ .Site.Title }}</description>
{{ with .Site.Params.copyright }}
<copyright>{{ . }}</copyright>{{ end }}
<generator>Hugo (https://gohugo.io)</generator>
{{ with .Site.Language.Locale }}
<language>{{.}}</language>{{end}}
{{ with .Site.Params.author.name }}
<managingEditor>{{ with $.Site.Params.author.email }}{{ . }}{{ with $.Site.Params.author.name }} ({{ . }}){{ end }}{{ end }}</managingEditor>{{ end }}
{{ with .Site.Params.author.name }}
<webMaster>{{ with $.Site.Params.author.email }}{{ . }}{{ with $.Site.Params.author.name }} ({{ . }}){{ end }}{{ end }}</webMaster>{{ end }}
{{ if not .Date.IsZero }}
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 Z0700" | safeHTML }}</lastBuildDate>{{ end }}
{{ with .OutputFormats.Get "RSS" }}
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}{{ end }}
{{ range $index, $page := $pages }}
<item>
<title>{{ .Title }}</title>
<link>{{ .Permalink }}</link>
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 Z0700" | safeHTML }}</pubDate>
{{ with .Site.Params.author.name }}
<author>{{ with $.Site.Params.author.email }}{{ . }}{{ with $.Site.Params.author.name }} ({{ . }}){{ end }}{{ end }}</author>{{ end }}
{{ $cover := "" }}
{{ with $page.Params.cover }}
{{ $res := $page.Resources.GetMatch . }}
{{ if $res }}
{{ $cover = $res.Permalink }}
{{ else }}
{{ $cover = . | absURL }}
{{ end }}
{{ end }}
{{ if $cover }}
<media:content url="{{ $cover }}" medium="image" />
<media:title>Cover Image</media:title>
{{ end }}
<description>
{{ .Summary | plainify | htmlEscape }}
</description>
<guid isPermaLink="true">{{ .Permalink }}</guid>
</item>
{{ end }}
</channel>
</rss>

View File

@ -1,24 +0,0 @@
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
{{ range where .Data.Pages "Params.private" "!=" true }}
{{- if .Permalink -}}
<url>
<loc>{{ .Permalink }}</loc>{{ if not .Lastmod.IsZero }}
<lastmod>{{ safeHTML ( .Lastmod.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
<changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
<priority>{{ .Sitemap.Priority }}</priority>{{ end }}{{ if .IsTranslated }}{{ range .Translations }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.Label }}"
href="{{ .Permalink }}"
/>{{ end }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.Label }}"
href="{{ .Permalink }}"
/>{{ end }}
</url>
{{- end -}}
{{ end }}
</urlset>

View File

@ -1,37 +1,47 @@
<!-- Basic stuff -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="HandheldFriendly" content="True"> <meta name="HandheldFriendly" content="True">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer-when-downgrade"> <meta name="referrer" content="no-referrer-when-downgrade">
<meta name="theme-name" content="Anubis2">
{{ partial "title.html" . }} {{ partial "title.html" . }}
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"> <meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}">
<meta name="author" content="{{ with .Params.author }}{{ . }}{{ else }}{{ with site.Params.author }}{{ . }}{{ end }}{{ end -}}">
<meta name="copyright" content="{{ with site.Params.copyright }}{{ . }}{{ else }}{{ with site.Params.author }}{{ . }}{{ end }}{{ end -}}">
{{ with .Keywords }} {{ with .Keywords }}
<meta name="keywords" content="{{ range $i, $e := . }}{{ if $i }},{{ end }}{{ $e }}{{ end }}"> <meta name="keywords" content="{{ range $i, $e := . }}{{ if $i }},{{ end }}{{ $e }}{{ end }}">
{{ end }} {{ end }}
{{ if and (.IsPage) (eq .Params.private true)}} {{ if and (.IsPage) (eq .Params.hidden true)}}
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
{{ end }} {{ end }}
{{ with .OutputFormats.Get "rss" -}} {{ with .OutputFormats.Get "rss" -}}
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .RelPermalink site.Title | safeHTML }} {{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .RelPermalink $.Site.Title | safeHTML }}
{{ end -}} {{ end -}}
{{ partial "head/favicons.html" . }} {{ partial "favicons.html" . }}
{{ $params := partial "params-helper.html" . }} {{ $colorTheme := "light" }}
{{ $colorThemeSwitcher := true }}
{{ if or (eq $params.enableColorThemeSwitcher true) (eq $params.colorTheme "auto") }} {{ if and (isset site.Params "colortheme") (ne site.Params.colortheme "") }}
{{ $colorTheme = site.Params.colortheme | lower }}
{{ end }}
{{ if isset site.Params "colorthemeswitcher" }}
{{ $colorThemeSwitcher = site.Params.colorthemeswitcher }}
{{ else }}
{{ $colorThemeSwitcher = true }}
{{ end }}
{{ if or (eq $colorThemeSwitcher true) (eq $colorTheme "auto") }}
<style> <style>
body { body {
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
} }
</style> </style>
<noscript> <noscript>
<style> <style>
body { body {
@ -40,33 +50,80 @@
} }
</style> </style>
</noscript> </noscript>
{{ end }} {{end}}
{{ partial "resource.html" (dict "context" . "type" "css" "filename" "css/main.css") }} {{ partial "resource.html" (dict "context" . "type" "css" "filename" "css/main.css") }}
{{ partial "resource.html" (dict "context" . "type" "css" "filename" "css/light.css") }} {{ partial "resource.html" (dict "context" . "type" "css" "filename" "css/light.css") }}
{{ partial "resource.html" (dict "context" . "type" "css" "filename" "css/dark.css") }} {{ partial "resource.html" (dict "context" . "type" "css" "filename" "css/dark.css") }}
{{ partial "features/custom-css.html" . }}
{{ partial "features/custom-js.html" . }}
{{ if $params.enableCopyCodeButton }} {{ $opts := dict "transpiler" "libsass" "targetPath" "css/style.css" }}
{{ range site.Params.customCSS }}
{{ $r := "" }}
{{ with resources.Get . }}
{{ $r = . }}
{{ else }}
{{ errorf "error: You defined customCSS, but %s was not found, please check your config." . }}
{{ end }}
{{ if $r}}
{{ with $r | toCSS $opts | minify | fingerprint }}
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{ end }}
{{ end }}
{{ end }}
{{ if site.Params.isso.enabled }} {{/* TODO: maybe we can remove it */}}
<style>
#isso-thread .textarea {
color: #000;
}
#isso-thread .isso-feedlink {
position: relative;
z-index: 1;
}
</style>
{{ end }}
{{ if .Site.Params.copyCodeButton | default true }}
{{ partial "resource.html" (dict "context" . "type" "js" "filename" "js/copy-code.js") }} {{ partial "resource.html" (dict "context" . "type" "js" "filename" "js/copy-code.js") }}
{{ end }} {{ end }}
{{ range $params.customJS -}} {{ range .Site.Params.customJS -}}
{{ partial "resource.html" (dict "context" $ "type" "js" "filename" . ) }} {{ partial "resource.html" (dict "context" $ "type" "js" "filename" . ) }}
{{- end }} {{- end }}
{{ if isset .Site.Params "webmentions" }}
{{ if isset .Site.Params.webmentions "login" }}
<link rel="webmention" href="https://webmention.io/{{.Site.Params.webmentions.login}}/webmention" />
{{ if eq .Site.Params.webmentions.pingback true }}
<link rel="pingback" href="https://webmention.io/{{.Site.Params.webmentions.login}}/xmlrpc" />
{{ end }}
{{ end }}
{{ if isset .Site.Params.webmentions "url" }}
<link rel="webmention" href="{{.Site.Params.webmentions.url}}" />
{{ end }}
{{ end }}
{{- if isset .Params "canonical" -}} {{- if isset .Params "canonical" -}}
<link rel="canonical" href="{{ .Params.canonical }}" /> <link rel="canonical" href="{{ .Params.canonical }}" />
{{- else -}} {{- else -}}
<link rel="canonical" href="{{ .Permalink }}" /> <link rel="canonical" href="{{ .Permalink }}" />
{{- end }} {{- end }}
<!-- Article tags -->
<!-- <meta property="article:published_time" content="">
<meta property="article:modified_time" content="">
<meta property="article:tag" content="">
<meta property="article:publisher" content="https://www.facebook.com/XXX"> -->
{{ if (.Site.Params.GoogleAnalytics.enabled) }}
{{ partial "google_analytics.html" . }}
{{ end }}
{{ if (.Site.Params.UmamiAnalytics.enabled ) }}
{{ partial "umami_analytics.html" . }}
{{ end }}
{{ partial "head-extra.html" . }} {{ partial "head-extra.html" . }}
<script defer src="https://umami.thanwer.com/script.js" data-website-id="eded536d-a598-471a-945b-faaebc4837a9"></script>
{{ if hugo.IsProduction }}
{{ if isset $params.services "umami" }}
{{ partial "analytics/umami_analytics.html" . }}
{{ end }}
{{ end }}

View File

@ -1,15 +0,0 @@
<ul class="language-select">
{{ $pageLang := .Page.Lang}}
{{ $translations := .AllTranslations }}
{{ if not .IsTranslated }}
{{ $translations = site.Home.AllTranslations }}
{{ end }}
{{ range $translations }}
{{ if eq .Lang $pageLang }}
<li>{{ .Language.Label }}</li>
{{ else }}
<li><a href="{{ .RelPermalink }}">{{ .Language.Label }}</a></li>
{{ end }}
{{ end }}
</ul>

View File

@ -1,56 +0,0 @@
{{ $dateFormat := "2006-01-02" }}
{{ if site.Params.dateFormat }}
{{ $dateFormat = site.Params.dateFormat }}
{{ end }}
{{ $machineDateFormat := "2006-01-02" }}
{{ $lastmod := .Lastmod }}
{{ if .Params.lastmod }}
{{ $lastmod = .Params.lastmod }}
{{ end }}
<div class="post-info noselect">
{{ if .Params.date }}
<div class="post-date dt-published">
<time datetime="{{ .Params.date.Format $machineDateFormat }}">
{{- .Params.date.Format $dateFormat -}}
</time>
{{ if and $lastmod (ne $lastmod .Date) -}}
[{{ i18n "lastMod" }} <time datetime="{{ $lastmod.Format $machineDateFormat }}">
{{- $lastmod.Format $dateFormat -}}
</time>]
{{- end }}
</div>
{{ end }}
<a class="post-hidden-url u-url" href="{{ .RelPermalink }}">{{ .RelPermalink }}</a>
<a href="{{ site.BaseURL }}" class="p-name p-author post-hidden-author h-card" rel="me">{{ .Params.author | default site.Params.author }}</a>
<div class="post-taxonomies">
{{ if .Params.categories }}
<ul class="post-categories">
{{ range $cat_name := .Params.categories }}
{{ $cat_url := (printf "/categories/%s" $cat_name | urlize ) }}
<li><a href="{{ $cat_url }}">{{ $cat_name }}</a></li>
{{ end }}
</ul>
{{ end }}
{{ if .Params.tags }}
<ul class="post-tags">
{{ range $tag_name := .Params.tags }}
{{ $tag_url := (printf "/tags/%s" ($tag_name | urlize)) }}
<li><a href="{{ $tag_url }}">#{{ $tag_name }}</a></li>
{{ end }}
</ul>
{{ end }}
{{ if .Params.authors }}
<ul class="post-authors">
{{ range $author_name := .Params.authors }}
{{ $author := (site.GetPage (printf "/authors/%s" $author_name | urlize )) }}
<li><a href="{{ $author.RelPermalink }}">@{{ $author_name }}</a></li>
{{ end }}
</ul>
{{ end }}
</div>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

@ -1 +1 @@
Subproject commit 9c4e5b169d285c4c008a754bbd361181407323ed Subproject commit 95844a6f7a416d79ecc9e49034192e45fe0381aa