[{"data":1,"prerenderedAt":1475},["ShallowReactive",2],{"doc-en-\u002Fen\u002Fdocs\u002Fnetworking\u002Ffirewall":3,"docs-en-all":1405},{"id":4,"title":5,"body":6,"category":1387,"description":1388,"draft":1389,"extension":1390,"icon":1391,"lastReviewed":1392,"meta":1393,"navigation":247,"order":209,"path":1394,"prerequisites":1395,"readingTime":1396,"seo":1397,"stem":1398,"tags":1399,"__hash__":1404},"docs_en\u002Fen\u002Fdocs\u002Fnetworking\u002Ffirewall.md","Firewall configuration",{"type":7,"value":8,"toc":1371},"minimark",[9,13,18,134,137,160,169,173,184,188,191,557,560,564,749,753,756,1098,1102,1105,1110,1113,1197,1200,1204,1207,1224,1228,1231,1237,1241,1244,1259,1263,1266,1291,1294,1298,1301,1333,1344,1348,1367],[10,11,12],"p",{},"A healthy HeroCtl cluster has a small, fixed set of ports. This document shows which they are, what each one does, and how to configure the operating system firewall to open exactly what's needed, no more and no less.",[14,15,17],"h2",{"id":16},"ports-in-use","Ports in use",[19,20,21,40],"table",{},[22,23,24],"thead",{},[25,26,27,31,34,37],"tr",{},[28,29,30],"th",{},"Port",[28,32,33],{},"Protocol",[28,35,36],{},"Function",[28,38,39],{},"Who needs access",[41,42,43,58,70,83,96,109,121],"tbody",{},[25,44,45,49,52,55],{},[46,47,48],"td",{},"80",[46,50,51],{},"TCP",[46,53,54],{},"Ingress router (HTTP)",[46,56,57],{},"The whole internet",[25,59,60,63,65,68],{},[46,61,62],{},"443",[46,64,51],{},[46,66,67],{},"Ingress router (HTTPS)",[46,69,57],{},[25,71,72,75,77,80],{},[46,73,74],{},"8080",[46,76,51],{},[46,78,79],{},"Control plane API",[46,81,82],{},"Operators and CLI, via VPN or allowlist",[25,84,85,88,90,93],{},[46,86,87],{},"8443",[46,89,51],{},[46,91,92],{},"TLS web panel",[46,94,95],{},"Operators, via VPN or allowlist",[25,97,98,101,103,106],{},[46,99,100],{},"4646",[46,102,51],{},[46,104,105],{},"Internal coordination between nodes (consensus)",[46,107,108],{},"Only other cluster nodes",[25,110,111,114,116,119],{},[46,112,113],{},"4647",[46,115,51],{},[46,117,118],{},"RPC between coordinator and workers",[46,120,108],{},[25,122,123,126,129,132],{},[46,124,125],{},"4648",[46,127,128],{},"TCP+UDP",[46,130,131],{},"Gossip between nodes",[46,133,108],{},[10,135,136],{},"The general rule is simple:",[138,139,140,148,154],"ul",{},[141,142,143,147],"li",{},[144,145,146],"strong",{},"80 and 443"," open to the world. They are the entry point for your applications.",[141,149,150,153],{},[144,151,152],{},"8080 and 8443"," should never be open to the public internet.",[141,155,156,159],{},[144,157,158],{},"4646, 4647, and 4648"," stay restricted to the cluster's internal IPs.",[161,162,163],"blockquote",{},[10,164,165,168],{},[144,166,167],{},"Warning:"," Exposing 8080 without an allowlist is the most common misconfiguration in new clusters. Anyone with the admin token can submit jobs. Treat this port like SSH.",[14,170,172],{"id":171},"recommended-topology","Recommended topology",[174,175,180],"pre",{"className":176,"code":178,"language":179},[177],"language-text","                     internet\n                        │\n                  ┌─────┴─────┐\n                  │  80, 443  │   ← any origin\n                  └─────┬─────┘\n                        │\n                ┌───────┴───────┐\n                │   cluster     │\n                │   nodes       │\n                └───────┬───────┘\n                        │\n                  ┌─────┴─────┐\n                  │ 4646-4648 │   ← internal IPs only\n                  └───────────┘\n                        │\n                  ┌─────┴─────┐\n                  │ 8080,8443 │   ← VPN or allowlist only\n                  └───────────┘\n","text",[181,182,178],"code",{"__ignoreMap":183},"",[14,185,187],{"id":186},"ubuntu-and-debian-ufw","Ubuntu and Debian (ufw)",[10,189,190],{},"Minimum configuration for a server node:",[174,192,196],{"className":193,"code":194,"language":195,"meta":183,"style":183},"language-bash shiki shiki-themes github-dark-default","# regras default\nsudo ufw default deny incoming\nsudo ufw default allow outgoing\n\n# acesso administrativo (ajuste o IP de origem)\nsudo ufw allow from 203.0.113.10 to any port 22 proto tcp comment 'SSH operador'\nsudo ufw allow from 203.0.113.10 to any port 8080 proto tcp comment 'API'\nsudo ufw allow from 203.0.113.10 to any port 8443 proto tcp comment 'Painel'\n\n# tráfego público\nsudo ufw allow 80\u002Ftcp comment 'Ingress HTTP'\nsudo ufw allow 443\u002Ftcp comment 'Ingress HTTPS'\n\n# comunicação interna entre nós (substitua pelos IPs reais)\nfor ip in 10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4; do\n  sudo ufw allow from $ip to any port 4646 proto tcp\n  sudo ufw allow from $ip to any port 4647 proto tcp\n  sudo ufw allow from $ip to any port 4648\ndone\n\nsudo ufw enable\nsudo ufw status numbered\n","bash",[181,197,198,207,227,242,249,255,295,326,357,362,368,385,402,407,413,445,475,501,523,529,534,544],{"__ignoreMap":183},[199,200,203],"span",{"class":201,"line":202},"line",1,[199,204,206],{"class":205},"sH3jZ","# regras default\n",[199,208,210,214,218,221,224],{"class":201,"line":209},2,[199,211,213],{"class":212},"sQhOw","sudo",[199,215,217],{"class":216},"s9uIt"," ufw",[199,219,220],{"class":216}," default",[199,222,223],{"class":216}," deny",[199,225,226],{"class":216}," incoming\n",[199,228,230,232,234,236,239],{"class":201,"line":229},3,[199,231,213],{"class":212},[199,233,217],{"class":216},[199,235,220],{"class":216},[199,237,238],{"class":216}," allow",[199,240,241],{"class":216}," outgoing\n",[199,243,245],{"class":201,"line":244},4,[199,246,248],{"emptyLinePlaceholder":247},true,"\n",[199,250,252],{"class":201,"line":251},5,[199,253,254],{"class":205},"# acesso administrativo (ajuste o IP de origem)\n",[199,256,258,260,262,264,267,271,274,277,280,283,286,289,292],{"class":201,"line":257},6,[199,259,213],{"class":212},[199,261,217],{"class":216},[199,263,238],{"class":216},[199,265,266],{"class":216}," from",[199,268,270],{"class":269},"sFSAA"," 203.0.113.10",[199,272,273],{"class":216}," to",[199,275,276],{"class":216}," any",[199,278,279],{"class":216}," port",[199,281,282],{"class":269}," 22",[199,284,285],{"class":216}," proto",[199,287,288],{"class":216}," tcp",[199,290,291],{"class":216}," comment",[199,293,294],{"class":216}," 'SSH operador'\n",[199,296,298,300,302,304,306,308,310,312,314,317,319,321,323],{"class":201,"line":297},7,[199,299,213],{"class":212},[199,301,217],{"class":216},[199,303,238],{"class":216},[199,305,266],{"class":216},[199,307,270],{"class":269},[199,309,273],{"class":216},[199,311,276],{"class":216},[199,313,279],{"class":216},[199,315,316],{"class":269}," 8080",[199,318,285],{"class":216},[199,320,288],{"class":216},[199,322,291],{"class":216},[199,324,325],{"class":216}," 'API'\n",[199,327,329,331,333,335,337,339,341,343,345,348,350,352,354],{"class":201,"line":328},8,[199,330,213],{"class":212},[199,332,217],{"class":216},[199,334,238],{"class":216},[199,336,266],{"class":216},[199,338,270],{"class":269},[199,340,273],{"class":216},[199,342,276],{"class":216},[199,344,279],{"class":216},[199,346,347],{"class":269}," 8443",[199,349,285],{"class":216},[199,351,288],{"class":216},[199,353,291],{"class":216},[199,355,356],{"class":216}," 'Painel'\n",[199,358,360],{"class":201,"line":359},9,[199,361,248],{"emptyLinePlaceholder":247},[199,363,365],{"class":201,"line":364},10,[199,366,367],{"class":205},"# tráfego público\n",[199,369,371,373,375,377,380,382],{"class":201,"line":370},11,[199,372,213],{"class":212},[199,374,217],{"class":216},[199,376,238],{"class":216},[199,378,379],{"class":216}," 80\u002Ftcp",[199,381,291],{"class":216},[199,383,384],{"class":216}," 'Ingress HTTP'\n",[199,386,388,390,392,394,397,399],{"class":201,"line":387},12,[199,389,213],{"class":212},[199,391,217],{"class":216},[199,393,238],{"class":216},[199,395,396],{"class":216}," 443\u002Ftcp",[199,398,291],{"class":216},[199,400,401],{"class":216}," 'Ingress HTTPS'\n",[199,403,405],{"class":201,"line":404},13,[199,406,248],{"emptyLinePlaceholder":247},[199,408,410],{"class":201,"line":409},14,[199,411,412],{"class":205},"# comunicação interna entre nós (substitua pelos IPs reais)\n",[199,414,416,420,424,427,430,433,436,439,442],{"class":201,"line":415},15,[199,417,419],{"class":418},"suJrU","for",[199,421,423],{"class":422},"sZEs4"," ip ",[199,425,426],{"class":418},"in",[199,428,429],{"class":216}," 10.0.0.1",[199,431,432],{"class":216}," 10.0.0.2",[199,434,435],{"class":216}," 10.0.0.3",[199,437,438],{"class":216}," 10.0.0.4",[199,440,441],{"class":422},"; ",[199,443,444],{"class":418},"do\n",[199,446,448,451,453,455,457,460,463,465,467,470,472],{"class":201,"line":447},16,[199,449,450],{"class":212},"  sudo",[199,452,217],{"class":216},[199,454,238],{"class":216},[199,456,266],{"class":216},[199,458,459],{"class":422}," $ip ",[199,461,462],{"class":216},"to",[199,464,276],{"class":216},[199,466,279],{"class":216},[199,468,469],{"class":269}," 4646",[199,471,285],{"class":216},[199,473,474],{"class":216}," tcp\n",[199,476,478,480,482,484,486,488,490,492,494,497,499],{"class":201,"line":477},17,[199,479,450],{"class":212},[199,481,217],{"class":216},[199,483,238],{"class":216},[199,485,266],{"class":216},[199,487,459],{"class":422},[199,489,462],{"class":216},[199,491,276],{"class":216},[199,493,279],{"class":216},[199,495,496],{"class":269}," 4647",[199,498,285],{"class":216},[199,500,474],{"class":216},[199,502,504,506,508,510,512,514,516,518,520],{"class":201,"line":503},18,[199,505,450],{"class":212},[199,507,217],{"class":216},[199,509,238],{"class":216},[199,511,266],{"class":216},[199,513,459],{"class":422},[199,515,462],{"class":216},[199,517,276],{"class":216},[199,519,279],{"class":216},[199,521,522],{"class":269}," 4648\n",[199,524,526],{"class":201,"line":525},19,[199,527,528],{"class":418},"done\n",[199,530,532],{"class":201,"line":531},20,[199,533,248],{"emptyLinePlaceholder":247},[199,535,537,539,541],{"class":201,"line":536},21,[199,538,213],{"class":212},[199,540,217],{"class":216},[199,542,543],{"class":216}," enable\n",[199,545,547,549,551,554],{"class":201,"line":546},22,[199,548,213],{"class":212},[199,550,217],{"class":216},[199,552,553],{"class":216}," status",[199,555,556],{"class":216}," numbered\n",[10,558,559],{},"On worker-only nodes (no exposed panel), skip the 8080 and 8443 lines.",[14,561,563],{"id":562},"rhel-fedora-almalinux-firewalld","RHEL, Fedora, AlmaLinux (firewalld)",[174,565,567],{"className":193,"code":566,"language":195,"meta":183,"style":183},"# zonas separadas: pública para 80\u002F443, interna para portas de cluster\nsudo firewall-cmd --permanent --zone=public --add-service=http\nsudo firewall-cmd --permanent --zone=public --add-service=https\n\n# zona internal recebe os IPs do cluster\nsudo firewall-cmd --permanent --zone=internal --add-source=10.0.0.0\u002F24\nsudo firewall-cmd --permanent --zone=internal --add-port=4646\u002Ftcp\nsudo firewall-cmd --permanent --zone=internal --add-port=4647\u002Ftcp\nsudo firewall-cmd --permanent --zone=internal --add-port=4648\u002Ftcp\nsudo firewall-cmd --permanent --zone=internal --add-port=4648\u002Fudp\n\n# acesso admin: zone trusted com IP do operador\nsudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.10\nsudo firewall-cmd --permanent --zone=trusted --add-port=8080\u002Ftcp\nsudo firewall-cmd --permanent --zone=trusted --add-port=8443\u002Ftcp\n\nsudo firewall-cmd --reload\nsudo firewall-cmd --list-all-zones\n",[181,568,569,574,590,603,607,612,626,639,652,665,678,682,687,701,714,727,731,740],{"__ignoreMap":183},[199,570,571],{"class":201,"line":202},[199,572,573],{"class":205},"# zonas separadas: pública para 80\u002F443, interna para portas de cluster\n",[199,575,576,578,581,584,587],{"class":201,"line":209},[199,577,213],{"class":212},[199,579,580],{"class":216}," firewall-cmd",[199,582,583],{"class":269}," --permanent",[199,585,586],{"class":269}," --zone=public",[199,588,589],{"class":269}," --add-service=http\n",[199,591,592,594,596,598,600],{"class":201,"line":229},[199,593,213],{"class":212},[199,595,580],{"class":216},[199,597,583],{"class":269},[199,599,586],{"class":269},[199,601,602],{"class":269}," --add-service=https\n",[199,604,605],{"class":201,"line":244},[199,606,248],{"emptyLinePlaceholder":247},[199,608,609],{"class":201,"line":251},[199,610,611],{"class":205},"# zona internal recebe os IPs do cluster\n",[199,613,614,616,618,620,623],{"class":201,"line":257},[199,615,213],{"class":212},[199,617,580],{"class":216},[199,619,583],{"class":269},[199,621,622],{"class":269}," --zone=internal",[199,624,625],{"class":269}," --add-source=10.0.0.0\u002F24\n",[199,627,628,630,632,634,636],{"class":201,"line":297},[199,629,213],{"class":212},[199,631,580],{"class":216},[199,633,583],{"class":269},[199,635,622],{"class":269},[199,637,638],{"class":269}," --add-port=4646\u002Ftcp\n",[199,640,641,643,645,647,649],{"class":201,"line":328},[199,642,213],{"class":212},[199,644,580],{"class":216},[199,646,583],{"class":269},[199,648,622],{"class":269},[199,650,651],{"class":269}," --add-port=4647\u002Ftcp\n",[199,653,654,656,658,660,662],{"class":201,"line":359},[199,655,213],{"class":212},[199,657,580],{"class":216},[199,659,583],{"class":269},[199,661,622],{"class":269},[199,663,664],{"class":269}," --add-port=4648\u002Ftcp\n",[199,666,667,669,671,673,675],{"class":201,"line":364},[199,668,213],{"class":212},[199,670,580],{"class":216},[199,672,583],{"class":269},[199,674,622],{"class":269},[199,676,677],{"class":269}," --add-port=4648\u002Fudp\n",[199,679,680],{"class":201,"line":370},[199,681,248],{"emptyLinePlaceholder":247},[199,683,684],{"class":201,"line":387},[199,685,686],{"class":205},"# acesso admin: zone trusted com IP do operador\n",[199,688,689,691,693,695,698],{"class":201,"line":404},[199,690,213],{"class":212},[199,692,580],{"class":216},[199,694,583],{"class":269},[199,696,697],{"class":269}," --zone=trusted",[199,699,700],{"class":269}," --add-source=203.0.113.10\n",[199,702,703,705,707,709,711],{"class":201,"line":409},[199,704,213],{"class":212},[199,706,580],{"class":216},[199,708,583],{"class":269},[199,710,697],{"class":269},[199,712,713],{"class":269}," --add-port=8080\u002Ftcp\n",[199,715,716,718,720,722,724],{"class":201,"line":415},[199,717,213],{"class":212},[199,719,580],{"class":216},[199,721,583],{"class":269},[199,723,697],{"class":269},[199,725,726],{"class":269}," --add-port=8443\u002Ftcp\n",[199,728,729],{"class":201,"line":447},[199,730,248],{"emptyLinePlaceholder":247},[199,732,733,735,737],{"class":201,"line":477},[199,734,213],{"class":212},[199,736,580],{"class":216},[199,738,739],{"class":269}," --reload\n",[199,741,742,744,746],{"class":201,"line":503},[199,743,213],{"class":212},[199,745,580],{"class":216},[199,747,748],{"class":269}," --list-all-zones\n",[14,750,752],{"id":751},"iptables-directly","iptables directly",[10,754,755],{},"If you prefer explicit rules, or use a system without ufw\u002Ffirewalld:",[174,757,759],{"className":193,"code":758,"language":195,"meta":183,"style":183},"# tráfego estabelecido\niptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT\niptables -A INPUT -i lo -j ACCEPT\n\n# ingress público\niptables -A INPUT -p tcp --dport 80 -j ACCEPT\niptables -A INPUT -p tcp --dport 443 -j ACCEPT\n\n# admin restrito\niptables -A INPUT -p tcp -s 203.0.113.10 --dport 22 -j ACCEPT\niptables -A INPUT -p tcp -s 203.0.113.10 --dport 8080 -j ACCEPT\niptables -A INPUT -p tcp -s 203.0.113.10 --dport 8443 -j ACCEPT\n\n# cluster interno (repita para cada IP)\niptables -A INPUT -p tcp -s 10.0.0.0\u002F24 --dport 4646 -j ACCEPT\niptables -A INPUT -p tcp -s 10.0.0.0\u002F24 --dport 4647 -j ACCEPT\niptables -A INPUT -p tcp -s 10.0.0.0\u002F24 --dport 4648 -j ACCEPT\niptables -A INPUT -p udp -s 10.0.0.0\u002F24 --dport 4648 -j ACCEPT\n\n# default deny\niptables -P INPUT DROP\n\n# persistir\nsudo netfilter-persistent save\n",[181,760,761,766,795,813,817,822,845,866,870,875,900,924,948,952,957,982,1006,1031,1056,1060,1065,1077,1081,1087],{"__ignoreMap":183},[199,762,763],{"class":201,"line":202},[199,764,765],{"class":205},"# tráfego estabelecido\n",[199,767,768,771,774,777,780,783,786,789,792],{"class":201,"line":209},[199,769,770],{"class":212},"iptables",[199,772,773],{"class":269}," -A",[199,775,776],{"class":216}," INPUT",[199,778,779],{"class":269}," -m",[199,781,782],{"class":216}," state",[199,784,785],{"class":269}," --state",[199,787,788],{"class":216}," ESTABLISHED,RELATED",[199,790,791],{"class":269}," -j",[199,793,794],{"class":216}," ACCEPT\n",[199,796,797,799,801,803,806,809,811],{"class":201,"line":229},[199,798,770],{"class":212},[199,800,773],{"class":269},[199,802,776],{"class":216},[199,804,805],{"class":269}," -i",[199,807,808],{"class":216}," lo",[199,810,791],{"class":269},[199,812,794],{"class":216},[199,814,815],{"class":201,"line":244},[199,816,248],{"emptyLinePlaceholder":247},[199,818,819],{"class":201,"line":251},[199,820,821],{"class":205},"# ingress público\n",[199,823,824,826,828,830,833,835,838,841,843],{"class":201,"line":257},[199,825,770],{"class":212},[199,827,773],{"class":269},[199,829,776],{"class":216},[199,831,832],{"class":269}," -p",[199,834,288],{"class":216},[199,836,837],{"class":269}," --dport",[199,839,840],{"class":269}," 80",[199,842,791],{"class":269},[199,844,794],{"class":216},[199,846,847,849,851,853,855,857,859,862,864],{"class":201,"line":297},[199,848,770],{"class":212},[199,850,773],{"class":269},[199,852,776],{"class":216},[199,854,832],{"class":269},[199,856,288],{"class":216},[199,858,837],{"class":269},[199,860,861],{"class":269}," 443",[199,863,791],{"class":269},[199,865,794],{"class":216},[199,867,868],{"class":201,"line":328},[199,869,248],{"emptyLinePlaceholder":247},[199,871,872],{"class":201,"line":359},[199,873,874],{"class":205},"# admin restrito\n",[199,876,877,879,881,883,885,887,890,892,894,896,898],{"class":201,"line":364},[199,878,770],{"class":212},[199,880,773],{"class":269},[199,882,776],{"class":216},[199,884,832],{"class":269},[199,886,288],{"class":216},[199,888,889],{"class":269}," -s",[199,891,270],{"class":269},[199,893,837],{"class":269},[199,895,282],{"class":269},[199,897,791],{"class":269},[199,899,794],{"class":216},[199,901,902,904,906,908,910,912,914,916,918,920,922],{"class":201,"line":370},[199,903,770],{"class":212},[199,905,773],{"class":269},[199,907,776],{"class":216},[199,909,832],{"class":269},[199,911,288],{"class":216},[199,913,889],{"class":269},[199,915,270],{"class":269},[199,917,837],{"class":269},[199,919,316],{"class":269},[199,921,791],{"class":269},[199,923,794],{"class":216},[199,925,926,928,930,932,934,936,938,940,942,944,946],{"class":201,"line":387},[199,927,770],{"class":212},[199,929,773],{"class":269},[199,931,776],{"class":216},[199,933,832],{"class":269},[199,935,288],{"class":216},[199,937,889],{"class":269},[199,939,270],{"class":269},[199,941,837],{"class":269},[199,943,347],{"class":269},[199,945,791],{"class":269},[199,947,794],{"class":216},[199,949,950],{"class":201,"line":404},[199,951,248],{"emptyLinePlaceholder":247},[199,953,954],{"class":201,"line":409},[199,955,956],{"class":205},"# cluster interno (repita para cada IP)\n",[199,958,959,961,963,965,967,969,971,974,976,978,980],{"class":201,"line":415},[199,960,770],{"class":212},[199,962,773],{"class":269},[199,964,776],{"class":216},[199,966,832],{"class":269},[199,968,288],{"class":216},[199,970,889],{"class":269},[199,972,973],{"class":216}," 10.0.0.0\u002F24",[199,975,837],{"class":269},[199,977,469],{"class":269},[199,979,791],{"class":269},[199,981,794],{"class":216},[199,983,984,986,988,990,992,994,996,998,1000,1002,1004],{"class":201,"line":447},[199,985,770],{"class":212},[199,987,773],{"class":269},[199,989,776],{"class":216},[199,991,832],{"class":269},[199,993,288],{"class":216},[199,995,889],{"class":269},[199,997,973],{"class":216},[199,999,837],{"class":269},[199,1001,496],{"class":269},[199,1003,791],{"class":269},[199,1005,794],{"class":216},[199,1007,1008,1010,1012,1014,1016,1018,1020,1022,1024,1027,1029],{"class":201,"line":477},[199,1009,770],{"class":212},[199,1011,773],{"class":269},[199,1013,776],{"class":216},[199,1015,832],{"class":269},[199,1017,288],{"class":216},[199,1019,889],{"class":269},[199,1021,973],{"class":216},[199,1023,837],{"class":269},[199,1025,1026],{"class":269}," 4648",[199,1028,791],{"class":269},[199,1030,794],{"class":216},[199,1032,1033,1035,1037,1039,1041,1044,1046,1048,1050,1052,1054],{"class":201,"line":503},[199,1034,770],{"class":212},[199,1036,773],{"class":269},[199,1038,776],{"class":216},[199,1040,832],{"class":269},[199,1042,1043],{"class":216}," udp",[199,1045,889],{"class":269},[199,1047,973],{"class":216},[199,1049,837],{"class":269},[199,1051,1026],{"class":269},[199,1053,791],{"class":269},[199,1055,794],{"class":216},[199,1057,1058],{"class":201,"line":525},[199,1059,248],{"emptyLinePlaceholder":247},[199,1061,1062],{"class":201,"line":531},[199,1063,1064],{"class":205},"# default deny\n",[199,1066,1067,1069,1072,1074],{"class":201,"line":536},[199,1068,770],{"class":212},[199,1070,1071],{"class":269}," -P",[199,1073,776],{"class":216},[199,1075,1076],{"class":216}," DROP\n",[199,1078,1079],{"class":201,"line":546},[199,1080,248],{"emptyLinePlaceholder":247},[199,1082,1084],{"class":201,"line":1083},23,[199,1085,1086],{"class":205},"# persistir\n",[199,1088,1090,1092,1095],{"class":201,"line":1089},24,[199,1091,213],{"class":212},[199,1093,1094],{"class":216}," netfilter-persistent",[199,1096,1097],{"class":216}," save\n",[14,1099,1101],{"id":1100},"cloud-firewall-as-an-upper-layer","Cloud firewall as an upper layer",[10,1103,1104],{},"Even with ufw correctly set on each node, it's worth enabling the cloud provider firewall as a second layer. If ufw goes down due to a bad change, the cloud firewall keeps holding.",[1106,1107,1109],"h3",{"id":1108},"digitalocean-cloud-firewall","DigitalOcean Cloud Firewall",[10,1111,1112],{},"Create a firewall and assign it to all cluster droplets:",[19,1114,1115,1130],{},[22,1116,1117],{},[25,1118,1119,1122,1125,1128],{},[28,1120,1121],{},"Direction",[28,1123,1124],{},"Type",[28,1126,1127],{},"Source",[28,1129,30],{},[41,1131,1132,1145,1157,1172,1185],{},[25,1133,1134,1137,1139,1142],{},[46,1135,1136],{},"Inbound",[46,1138,51],{},[46,1140,1141],{},"Anywhere",[46,1143,1144],{},"80, 443",[25,1146,1147,1149,1151,1154],{},[46,1148,1136],{},[46,1150,51],{},[46,1152,1153],{},"Operator IPs",[46,1155,1156],{},"22, 8080, 8443",[25,1158,1159,1161,1163,1169],{},[46,1160,1136],{},[46,1162,51],{},[46,1164,1165,1166],{},"Tag ",[181,1167,1168],{},"heroctl-cluster",[46,1170,1171],{},"4646, 4647, 4648",[25,1173,1174,1176,1179,1183],{},[46,1175,1136],{},[46,1177,1178],{},"UDP",[46,1180,1165,1181],{},[181,1182,1168],{},[46,1184,125],{},[25,1186,1187,1190,1193,1195],{},[46,1188,1189],{},"Outbound",[46,1191,1192],{},"All",[46,1194,1141],{},[46,1196,1192],{},[10,1198,1199],{},"Use tags instead of IPs for 4646-4648 — when you add a new node with the tag, it joins the rule automatically.",[1106,1201,1203],{"id":1202},"aws-security-groups","AWS Security Groups",[10,1205,1206],{},"Create two security groups:",[138,1208,1209,1219],{},[141,1210,1211,1214,1215,1218],{},[181,1212,1213],{},"heroctl-public",": 80 and 443 from ",[181,1216,1217],{},"0.0.0.0\u002F0",". Assign to all nodes.",[141,1220,1221,1223],{},[181,1222,1168],{},": 4646-4648 with source set to the security group itself (self-reference). For 8080 and 8443, source the bastion's security group.",[1106,1225,1227],{"id":1226},"hetzner-cloud-firewall","Hetzner Cloud Firewall",[10,1229,1230],{},"Hetzner has no self-reference. Use the project's private network and open by CIDR:",[174,1232,1235],{"className":1233,"code":1234,"language":179},[177],"allow tcp 80,443 from 0.0.0.0\u002F0\nallow tcp 4646,4647,4648 from 10.0.0.0\u002F16\nallow udp 4648 from 10.0.0.0\u002F16\nallow tcp 8080,8443 from \u003Cip-do-operador>\u002F32\n",[181,1236,1234],{"__ignoreMap":183},[14,1238,1240],{"id":1239},"cloudflare-in-front","Cloudflare in front",[10,1242,1243],{},"For protection against volumetric attacks, putting Cloudflare in proxy mode in front of the cluster works well. Watch out for:",[138,1245,1246,1249,1256],{},[141,1247,1248],{},"For certificate issuance, use DNS-01 (not HTTP-01). Proxy mode breaks HTTP-01.",[141,1250,1251,1252,1255],{},"Restrict ports 80 and 443 on the nodes to accept only Cloudflare IP ranges. The official list lives at ",[181,1253,1254],{},"https:\u002F\u002Fwww.cloudflare.com\u002Fips-v4",". Update via monthly cron.",[141,1257,1258],{},"Enable \"Full (strict)\" on Cloudflare. Don't use \"Flexible\" — that makes Cloudflare speak HTTP with your cluster even when the user has HTTPS.",[14,1260,1262],{"id":1261},"blocking-administrative-access","Blocking administrative access",[10,1264,1265],{},"The most important rule in this document. Port 8080 cannot be reachable from the public internet. Three viable paths:",[1267,1268,1269,1275,1281],"ol",{},[141,1270,1271,1274],{},[144,1272,1273],{},"VPN."," WireGuard or Tailscale between operator machines and the cluster. Block 8080 from any source outside the VPN network. Recommended for teams.",[141,1276,1277,1280],{},[144,1278,1279],{},"Fixed IP allowlist."," Works for solo operators with stable residential IPs or a bastion VPS.",[141,1282,1283,1286,1287,1290],{},[144,1284,1285],{},"SSH tunnel."," ",[181,1288,1289],{},"ssh -L 8080:localhost:8080 server"," every time you use the CLI. Works, but adds friction.",[10,1292,1293],{},"The combination most often seen in production is VPN + cloud firewall. The operator joins the VPN, the cloud firewall only allows 8080 from the VPN range, and the node's ufw does the same on the inside.",[14,1295,1297],{"id":1296},"validation","Validation",[10,1299,1300],{},"After applying the rules, validate from outside:",[174,1302,1304],{"className":193,"code":1303,"language":195,"meta":183,"style":183},"# from your machine, no VPN, against a cluster IP\nnmap -p 80,443,8080,8443,4646,4647,4648 \u003Cip-do-no>\n",[181,1305,1306,1311],{"__ignoreMap":183},[199,1307,1308],{"class":201,"line":202},[199,1309,1310],{"class":205},"# from your machine, no VPN, against a cluster IP\n",[199,1312,1313,1316,1318,1321,1324,1327,1330],{"class":201,"line":209},[199,1314,1315],{"class":212},"nmap",[199,1317,832],{"class":269},[199,1319,1320],{"class":216}," 80,443,8080,8443,4646,4647,4648",[199,1322,1323],{"class":418}," \u003C",[199,1325,1326],{"class":216},"ip-do-n",[199,1328,1329],{"class":422},"o",[199,1331,1332],{"class":418},">\n",[10,1334,1335,1336,1339,1340,1343],{},"Correct result: 80 and 443 open, all others closed (",[181,1337,1338],{},"closed"," or ",[181,1341,1342],{},"filtered",").",[14,1345,1347],{"id":1346},"next-steps","Next steps",[138,1349,1350,1359],{},[141,1351,1352,1353,1358],{},"Configure ",[1354,1355,1357],"a",{"href":1356},"\u002Fen\u002Fdocs\u002Fnetworking\u002Fingress-tls","ingress and TLS"," to start exposing applications.",[141,1360,1361,1362,1366],{},"Review ",[1354,1363,1365],{"href":1364},"\u002Fen\u002Fdocs\u002Fsecurity\u002Fsecrets","secret management",".",[1368,1369,1370],"style",{},"html pre.shiki code .sH3jZ, html code.shiki .sH3jZ{--shiki-default:#8B949E}html pre.shiki code .sQhOw, html code.shiki .sQhOw{--shiki-default:#FFA657}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}html pre.shiki code .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html pre.shiki code .suJrU, html code.shiki .suJrU{--shiki-default:#FF7B72}html pre.shiki code .sZEs4, html code.shiki .sZEs4{--shiki-default:#E6EDF3}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":183,"searchDepth":209,"depth":209,"links":1372},[1373,1374,1375,1376,1377,1378,1383,1384,1385,1386],{"id":16,"depth":209,"text":17},{"id":171,"depth":209,"text":172},{"id":186,"depth":209,"text":187},{"id":562,"depth":209,"text":563},{"id":751,"depth":209,"text":752},{"id":1100,"depth":209,"text":1101,"children":1379},[1380,1381,1382],{"id":1108,"depth":229,"text":1109},{"id":1202,"depth":229,"text":1203},{"id":1226,"depth":229,"text":1227},{"id":1239,"depth":209,"text":1240},{"id":1261,"depth":209,"text":1262},{"id":1296,"depth":209,"text":1297},{"id":1346,"depth":209,"text":1347},"rede","Which ports HeroCtl uses, which need to stay open, and which should never be exposed to the internet.",false,"md","i-lucide-shield","2026-04-26",{},"\u002Fen\u002Fdocs\u002Fnetworking\u002Ffirewall",[],"9 min read",{"title":5,"description":1388},"en\u002Fdocs\u002Fnetworking\u002Ffirewall",[1400,1401,770,1402,1403],"firewall","ufw","network","security","rbo8g5bOWXh5_nwdrOZfYMAtvwfBOcOX1KUmhGLLOGo",[1406,1412,1418,1423,1424,1428,1434,1439,1445,1450,1455,1459,1465,1469],{"path":1407,"title":1408,"description":1409,"category":1410,"order":202,"icon":1411},"\u002Fen\u002Fdocs\u002Fapi\u002Fapi-reference","REST API reference","Endpoints, JWT authentication, curl examples, and error patterns of the HeroCtl API.","api","i-lucide-code",{"path":1413,"title":1414,"description":1415,"category":1416,"order":202,"icon":1417},"\u002Fen\u002Fdocs\u002Fdeploy\u002Ffirst-deploy","Deploy your first app","Bring up a Node.js application with a Postgres database in 50 lines of YAML. Includes health check, rolling deploy, and rollback.","deploy","i-lucide-rocket",{"path":1419,"title":1420,"description":1421,"category":1416,"order":209,"icon":1422},"\u002Fen\u002Fdocs\u002Fdeploy\u002Frolling-canary-blue-green","Rolling, canary, blue-green, and rainbow","Four deploy strategies. When to use each, with complete examples and honest trade-offs.","i-lucide-git-branch",{"path":1394,"title":5,"description":1388,"category":1387,"order":209,"icon":1391},{"path":1356,"title":1425,"description":1426,"category":1387,"order":202,"icon":1427},"Ingress and automatic TLS","How to expose applications on port 443 with certificates issued and renewed automatically, without operating an external router.","i-lucide-globe",{"path":1429,"title":1430,"description":1431,"category":1432,"order":209,"icon":1433},"\u002Fen\u002Fdocs\u002Fobservability\u002Fbackup-restore","Backup and restore of cluster state","How to save, schedule, and restore HeroCtl control plane snapshots. Disaster recovery strategy.","observabilidade","i-lucide-archive",{"path":1435,"title":1436,"description":1437,"category":1432,"order":202,"icon":1438},"\u002Fen\u002Fdocs\u002Fobservability\u002Fmetrics-logs","Metrics and logs","Collect metrics, logs, and traces without standing up an external observability stack. When it's worth it, and when to integrate with an outside tool.","i-lucide-activity",{"path":1440,"title":1441,"description":1442,"category":1443,"order":229,"icon":1444},"\u002Fen\u002Fdocs\u002Foperations\u002Fcli-reference","Complete CLI reference","All heroctl commands with synopsis, flags, and example. Use as a desk reference.","operacoes","i-lucide-terminal",{"path":1446,"title":1447,"description":1448,"category":1443,"order":209,"icon":1449},"\u002Fen\u002Fdocs\u002Foperations\u002Ffirst-cluster","Bring up a 3-node cluster","Form a cluster with 3 servers in under 10 minutes. Tolerates 1-node failure with no downtime.","i-lucide-network",{"path":1451,"title":1452,"description":1453,"category":1443,"order":202,"icon":1454},"\u002Fen\u002Fdocs\u002Foperations\u002Finstallation","Installation","Install HeroCtl on any Linux server with Docker in a single command. Covers prerequisites, bootstrap, and verification.","i-lucide-download",{"path":1456,"title":1457,"description":1458,"category":1443,"order":244,"icon":1427},"\u002Fen\u002Fdocs\u002Foperations\u002Fmulti-region","Multi-region (planned for Q4 2026)","What to expect from multi-region in HeroCtl, how to run across regions today, and the roadmap through 2027.",{"path":1460,"title":1461,"description":1462,"category":1463,"order":209,"icon":1464},"\u002Fen\u002Fdocs\u002Fsecurity\u002Frbac","RBAC and access control (Business+)","Role, policy, and token model to limit who can submit, read, and operate the cluster.","seguranca","i-lucide-users",{"path":1364,"title":1466,"description":1467,"category":1463,"order":202,"icon":1468},"Secret management","How to keep passwords, tokens, and keys outside the job spec, with encryption at rest and versioned rotation.","i-lucide-key",{"path":1470,"title":1471,"description":1472,"category":1473,"order":202,"icon":1474},"\u002Fen\u002Fdocs\u002Ftroubleshooting\u002Fcommon-problems","Troubleshooting common problems","The 12 most frequent problems in HeroCtl clusters, with symptom, diagnosis, and step-by-step fix.","troubleshooting","i-lucide-alert-triangle",1777362181619]