[{"data":1,"prerenderedAt":1928},["ShallowReactive",2],{"page-\u002Flinux\u002F14-security-and-hardening":3},{"id":4,"title":5,"body":6,"description":1921,"extension":1922,"meta":1923,"navigation":519,"path":1924,"seo":1925,"stem":1926,"__hash__":1927},"content\u002Flinux\u002F14-security-and-hardening.md","14 — Security & Hardening",{"type":7,"value":8,"toc":1894},"minimark",[9,13,26,31,72,76,81,254,258,302,306,363,367,454,489,493,584,590,739,743,749,753,803,913,917,959,963,969,1067,1073,1227,1231,1309,1313,1382,1389,1393,1473,1477,1559,1563,1667,1671,1779,1783,1794,1890],[10,11,5],"h1",{"id":12},"_14-security-hardening",[14,15,16,17,21,22,25],"p",{},"Linux is relatively secure by default, but a stock install is not hardened. This chapter covers the defense-in-depth stack: firewall, SSH hardening, SELinux\u002FAppArmor, ",[18,19,20],"code",{},"fail2ban",", ",[18,23,24],{},"auditd",", capabilities, and the mindset of reducing attack surface. Security is layered — no single tool is sufficient.",[27,28,30],"h2",{"id":29},"the-security-mindset","The Security Mindset",[32,33,34,42,48,54,60,66],"ol",{},[35,36,37,41],"li",{},[38,39,40],"strong",{},"Reduce attack surface"," — uninstall what you don't need, close unused ports, disable unused services.",[35,43,44,47],{},[38,45,46],{},"Principle of least privilege"," — give users and services only the access they need.",[35,49,50,53],{},[38,51,52],{},"Defense in depth"," — multiple layers (firewall + app config + SELinux + monitoring).",[35,55,56,59],{},[38,57,58],{},"Patch promptly"," — security updates fix known vulnerabilities.",[35,61,62,65],{},[38,63,64],{},"Monitor and log"," — you can't respond to what you can't see.",[35,67,68,71],{},[38,69,70],{},"Assume breach"," — design so that one compromised service doesn't cascade.",[27,73,75],{"id":74},"firewall","Firewall",[77,78,80],"h3",{"id":79},"ufw-uncomplicated-firewall-ubuntu","UFW — Uncomplicated Firewall (Ubuntu)",[82,83,85],"code-wrapper",{"language":84},"bash",[86,87,91],"pre",{"className":88,"code":89,"language":84,"meta":90,"style":90},"language-bash shiki shiki-themes github-light github-dark","# Complex Implementation: production firewall with rate-limiting\nsudo ufw default deny incoming\nsudo ufw default allow outgoing\nsudo ufw allow 22\u002Ftcp           # SSH\nsudo ufw allow 80\u002Ftcp           # HTTP\nsudo ufw allow 443\u002Ftcp          # HTTPS\nsudo ufw limit 22\u002Ftcp           # rate-limit SSH (blocks brute force)\nsudo ufw allow from 192.168.1.0\u002F24 to any port 5432  # LAN to PostgreSQL\nsudo ufw --force enable\nsudo ufw status verbose\n","",[18,92,93,102,122,137,152,167,182,197,228,241],{"__ignoreMap":90},[94,95,98],"span",{"class":96,"line":97},"line",1,[94,99,101],{"class":100},"sdCPZ","# Complex Implementation: production firewall with rate-limiting\n",[94,103,105,109,113,116,119],{"class":96,"line":104},2,[94,106,108],{"class":107},"sIsaT","sudo",[94,110,112],{"class":111},"sJ6F3"," ufw",[94,114,115],{"class":111}," default",[94,117,118],{"class":111}," deny",[94,120,121],{"class":111}," incoming\n",[94,123,125,127,129,131,134],{"class":96,"line":124},3,[94,126,108],{"class":107},[94,128,112],{"class":111},[94,130,115],{"class":111},[94,132,133],{"class":111}," allow",[94,135,136],{"class":111}," outgoing\n",[94,138,140,142,144,146,149],{"class":96,"line":139},4,[94,141,108],{"class":107},[94,143,112],{"class":111},[94,145,133],{"class":111},[94,147,148],{"class":111}," 22\u002Ftcp",[94,150,151],{"class":100},"           # SSH\n",[94,153,155,157,159,161,164],{"class":96,"line":154},5,[94,156,108],{"class":107},[94,158,112],{"class":111},[94,160,133],{"class":111},[94,162,163],{"class":111}," 80\u002Ftcp",[94,165,166],{"class":100},"           # HTTP\n",[94,168,170,172,174,176,179],{"class":96,"line":169},6,[94,171,108],{"class":107},[94,173,112],{"class":111},[94,175,133],{"class":111},[94,177,178],{"class":111}," 443\u002Ftcp",[94,180,181],{"class":100},"          # HTTPS\n",[94,183,185,187,189,192,194],{"class":96,"line":184},7,[94,186,108],{"class":107},[94,188,112],{"class":111},[94,190,191],{"class":111}," limit",[94,193,148],{"class":111},[94,195,196],{"class":100},"           # rate-limit SSH (blocks brute force)\n",[94,198,200,202,204,206,209,212,215,218,221,225],{"class":96,"line":199},8,[94,201,108],{"class":107},[94,203,112],{"class":111},[94,205,133],{"class":111},[94,207,208],{"class":111}," from",[94,210,211],{"class":111}," 192.168.1.0\u002F24",[94,213,214],{"class":111}," to",[94,216,217],{"class":111}," any",[94,219,220],{"class":111}," port",[94,222,224],{"class":223},"snvgF"," 5432",[94,226,227],{"class":100},"  # LAN to PostgreSQL\n",[94,229,231,233,235,238],{"class":96,"line":230},9,[94,232,108],{"class":107},[94,234,112],{"class":111},[94,236,237],{"class":223}," --force",[94,239,240],{"class":111}," enable\n",[94,242,244,246,248,251],{"class":96,"line":243},10,[94,245,108],{"class":107},[94,247,112],{"class":111},[94,249,250],{"class":111}," status",[94,252,253],{"class":111}," verbose\n",[77,255,257],{"id":256},"caveat-always-allow-ssh-before-enabling-ufw","Caveat: Always Allow SSH Before Enabling UFW",[82,259,260],{"language":84},[86,261,263],{"className":88,"code":262,"language":84,"meta":90,"style":90},"# NAIVE: enable firewall without allowing SSH → locked out\nsudo ufw enable\n# PRODUCTION: allow SSH first\nsudo ufw allow 22\u002Ftcp\nsudo ufw enable\n",[18,264,265,270,278,283,294],{"__ignoreMap":90},[94,266,267],{"class":96,"line":97},[94,268,269],{"class":100},"# NAIVE: enable firewall without allowing SSH → locked out\n",[94,271,272,274,276],{"class":96,"line":104},[94,273,108],{"class":107},[94,275,112],{"class":111},[94,277,240],{"class":111},[94,279,280],{"class":96,"line":124},[94,281,282],{"class":100},"# PRODUCTION: allow SSH first\n",[94,284,285,287,289,291],{"class":96,"line":139},[94,286,108],{"class":107},[94,288,112],{"class":111},[94,290,133],{"class":111},[94,292,293],{"class":111}," 22\u002Ftcp\n",[94,295,296,298,300],{"class":96,"line":154},[94,297,108],{"class":107},[94,299,112],{"class":111},[94,301,240],{"class":111},[77,303,305],{"id":304},"firewalld-rhel","firewalld (RHEL)",[82,307,308],{"language":84},[86,309,311],{"className":88,"code":310,"language":84,"meta":90,"style":90},"sudo firewall-cmd --zone=public --add-service=http --permanent\nsudo firewall-cmd --zone=public --add-service=https --permanent\nsudo firewall-cmd --reload      # apply permanent rules\nsudo firewall-cmd --list-all\n",[18,312,313,329,342,354],{"__ignoreMap":90},[94,314,315,317,320,323,326],{"class":96,"line":97},[94,316,108],{"class":107},[94,318,319],{"class":111}," firewall-cmd",[94,321,322],{"class":223}," --zone=public",[94,324,325],{"class":223}," --add-service=http",[94,327,328],{"class":223}," --permanent\n",[94,330,331,333,335,337,340],{"class":96,"line":104},[94,332,108],{"class":107},[94,334,319],{"class":111},[94,336,322],{"class":223},[94,338,339],{"class":223}," --add-service=https",[94,341,328],{"class":223},[94,343,344,346,348,351],{"class":96,"line":124},[94,345,108],{"class":107},[94,347,319],{"class":111},[94,349,350],{"class":223}," --reload",[94,352,353],{"class":100},"      # apply permanent rules\n",[94,355,356,358,360],{"class":96,"line":139},[94,357,108],{"class":107},[94,359,319],{"class":111},[94,361,362],{"class":223}," --list-all\n",[27,364,366],{"id":365},"ssh-hardening","SSH Hardening",[82,368,369],{"language":84},[86,370,372],{"className":88,"code":371,"language":84,"meta":90,"style":90},"# \u002Fetc\u002Fssh\u002Fsshd_config\nPermitRootLogin no                # no root login via SSH\nPasswordAuthentication no         # require key auth (after setting up keys!)\nPubkeyAuthentication yes\nAllowUsers alice bob              # whitelist (optional)\nMaxAuthTries 3                    # limit password guesses\nClientAliveInterval 300           # idle timeout (5 min)\nX11Forwarding no                  # disable if not needed\n",[18,373,374,379,390,400,408,422,433,444],{"__ignoreMap":90},[94,375,376],{"class":96,"line":97},[94,377,378],{"class":100},"# \u002Fetc\u002Fssh\u002Fsshd_config\n",[94,380,381,384,387],{"class":96,"line":104},[94,382,383],{"class":107},"PermitRootLogin",[94,385,386],{"class":111}," no",[94,388,389],{"class":100},"                # no root login via SSH\n",[94,391,392,395,397],{"class":96,"line":124},[94,393,394],{"class":107},"PasswordAuthentication",[94,396,386],{"class":111},[94,398,399],{"class":100},"         # require key auth (after setting up keys!)\n",[94,401,402,405],{"class":96,"line":139},[94,403,404],{"class":107},"PubkeyAuthentication",[94,406,407],{"class":111}," yes\n",[94,409,410,413,416,419],{"class":96,"line":154},[94,411,412],{"class":107},"AllowUsers",[94,414,415],{"class":111}," alice",[94,417,418],{"class":111}," bob",[94,420,421],{"class":100},"              # whitelist (optional)\n",[94,423,424,427,430],{"class":96,"line":169},[94,425,426],{"class":107},"MaxAuthTries",[94,428,429],{"class":223}," 3",[94,431,432],{"class":100},"                    # limit password guesses\n",[94,434,435,438,441],{"class":96,"line":184},[94,436,437],{"class":107},"ClientAliveInterval",[94,439,440],{"class":223}," 300",[94,442,443],{"class":100},"           # idle timeout (5 min)\n",[94,445,446,449,451],{"class":96,"line":199},[94,447,448],{"class":107},"X11Forwarding",[94,450,386],{"class":111},[94,452,453],{"class":100},"                  # disable if not needed\n",[82,455,456],{"language":84},[86,457,459],{"className":88,"code":458,"language":84,"meta":90,"style":90},"sudo sshd -t                      # test config (don't restart if errors!)\nsudo systemctl reload sshd        # reload without dropping connections\n",[18,460,461,474],{"__ignoreMap":90},[94,462,463,465,468,471],{"class":96,"line":97},[94,464,108],{"class":107},[94,466,467],{"class":111}," sshd",[94,469,470],{"class":223}," -t",[94,472,473],{"class":100},"                      # test config (don't restart if errors!)\n",[94,475,476,478,481,484,486],{"class":96,"line":104},[94,477,108],{"class":107},[94,479,480],{"class":111}," systemctl",[94,482,483],{"class":111}," reload",[94,485,467],{"class":111},[94,487,488],{"class":100},"        # reload without dropping connections\n",[77,490,492],{"id":491},"edge-case-never-disable-password-auth-before-verifying-keys","Edge Case: Never Disable Password Auth Before Verifying Keys",[82,494,495],{"language":84},[86,496,498],{"className":88,"code":497,"language":84,"meta":90,"style":90},"# NAIVE: disable password auth, then realize keys don't work → locked out\n# PasswordAuthentication no\n# sudo systemctl reload sshd\n\n# PRODUCTION: verify key auth FIRST, then disable passwords\nssh -i ~\u002F.ssh\u002Fid_ed25519 alice@server   # test key login\n# If it works, THEN:\nsudo sed -i 's\u002F^#*PasswordAuthentication.*\u002FPasswordAuthentication no\u002F' \u002Fetc\u002Fssh\u002Fsshd_config\nsudo sshd -t && sudo systemctl reload sshd\n",[18,499,500,505,510,515,521,526,543,548,563],{"__ignoreMap":90},[94,501,502],{"class":96,"line":97},[94,503,504],{"class":100},"# NAIVE: disable password auth, then realize keys don't work → locked out\n",[94,506,507],{"class":96,"line":104},[94,508,509],{"class":100},"# PasswordAuthentication no\n",[94,511,512],{"class":96,"line":124},[94,513,514],{"class":100},"# sudo systemctl reload sshd\n",[94,516,517],{"class":96,"line":139},[94,518,520],{"emptyLinePlaceholder":519},true,"\n",[94,522,523],{"class":96,"line":154},[94,524,525],{"class":100},"# PRODUCTION: verify key auth FIRST, then disable passwords\n",[94,527,528,531,534,537,540],{"class":96,"line":169},[94,529,530],{"class":107},"ssh",[94,532,533],{"class":223}," -i",[94,535,536],{"class":111}," ~\u002F.ssh\u002Fid_ed25519",[94,538,539],{"class":111}," alice@server",[94,541,542],{"class":100},"   # test key login\n",[94,544,545],{"class":96,"line":184},[94,546,547],{"class":100},"# If it works, THEN:\n",[94,549,550,552,555,557,560],{"class":96,"line":199},[94,551,108],{"class":107},[94,553,554],{"class":111}," sed",[94,556,533],{"class":223},[94,558,559],{"class":111}," 's\u002F^#*PasswordAuthentication.*\u002FPasswordAuthentication no\u002F'",[94,561,562],{"class":111}," \u002Fetc\u002Fssh\u002Fsshd_config\n",[94,564,565,567,569,571,575,577,579,581],{"class":96,"line":230},[94,566,108],{"class":107},[94,568,467],{"class":111},[94,570,470],{"class":223},[94,572,574],{"class":573},"ssxIu"," && ",[94,576,108],{"class":107},[94,578,480],{"class":111},[94,580,483],{"class":111},[94,582,583],{"class":111}," sshd\n",[27,585,587,589],{"id":586},"fail2ban-brute-force-protection",[18,588,20],{}," — Brute-Force Protection",[82,591,592],{"language":84},[86,593,595],{"className":88,"code":594,"language":84,"meta":90,"style":90},"# Complex Implementation: SSH brute-force protection with custom jail\n# \u002Fetc\u002Ffail2ban\u002Fjail.local (don't edit jail.conf — it's overwritten on update)\nsudo tee \u002Fetc\u002Ffail2ban\u002Fjail.local \u003C\u003C'EOF'\n[DEFAULT]\nbantime = 1h\nfindtime = 10m\nmaxretry = 5\nbanaction = ufw\n\n[sshd]\nenabled = true\nport = ssh\nmaxretry = 3\nbantime = 6h\nEOF\n\nsudo systemctl restart fail2ban\nsudo fail2ban-client status sshd      # see banned IPs\nsudo fail2ban-client set sshd unbanip 1.2.3.4\n",[18,596,597,602,607,624,629,634,639,644,649,653,658,664,670,676,682,688,693,706,721],{"__ignoreMap":90},[94,598,599],{"class":96,"line":97},[94,600,601],{"class":100},"# Complex Implementation: SSH brute-force protection with custom jail\n",[94,603,604],{"class":96,"line":104},[94,605,606],{"class":100},"# \u002Fetc\u002Ffail2ban\u002Fjail.local (don't edit jail.conf — it's overwritten on update)\n",[94,608,609,611,614,617,621],{"class":96,"line":124},[94,610,108],{"class":107},[94,612,613],{"class":111}," tee",[94,615,616],{"class":111}," \u002Fetc\u002Ffail2ban\u002Fjail.local",[94,618,620],{"class":619},"svdQ7"," \u003C\u003C",[94,622,623],{"class":111},"'EOF'\n",[94,625,626],{"class":96,"line":139},[94,627,628],{"class":111},"[DEFAULT]\n",[94,630,631],{"class":96,"line":154},[94,632,633],{"class":111},"bantime = 1h\n",[94,635,636],{"class":96,"line":169},[94,637,638],{"class":111},"findtime = 10m\n",[94,640,641],{"class":96,"line":184},[94,642,643],{"class":111},"maxretry = 5\n",[94,645,646],{"class":96,"line":199},[94,647,648],{"class":111},"banaction = ufw\n",[94,650,651],{"class":96,"line":230},[94,652,520],{"emptyLinePlaceholder":519},[94,654,655],{"class":96,"line":243},[94,656,657],{"class":111},"[sshd]\n",[94,659,661],{"class":96,"line":660},11,[94,662,663],{"class":111},"enabled = true\n",[94,665,667],{"class":96,"line":666},12,[94,668,669],{"class":111},"port = ssh\n",[94,671,673],{"class":96,"line":672},13,[94,674,675],{"class":111},"maxretry = 3\n",[94,677,679],{"class":96,"line":678},14,[94,680,681],{"class":111},"bantime = 6h\n",[94,683,685],{"class":96,"line":684},15,[94,686,687],{"class":111},"EOF\n",[94,689,691],{"class":96,"line":690},16,[94,692,520],{"emptyLinePlaceholder":519},[94,694,696,698,700,703],{"class":96,"line":695},17,[94,697,108],{"class":107},[94,699,480],{"class":111},[94,701,702],{"class":111}," restart",[94,704,705],{"class":111}," fail2ban\n",[94,707,709,711,714,716,718],{"class":96,"line":708},18,[94,710,108],{"class":107},[94,712,713],{"class":111}," fail2ban-client",[94,715,250],{"class":111},[94,717,467],{"class":111},[94,719,720],{"class":100},"      # see banned IPs\n",[94,722,724,726,728,731,733,736],{"class":96,"line":723},19,[94,725,108],{"class":107},[94,727,713],{"class":111},[94,729,730],{"class":111}," set",[94,732,467],{"class":111},[94,734,735],{"class":111}," unbanip",[94,737,738],{"class":223}," 1.2.3.4\n",[27,740,742],{"id":741},"selinux-and-apparmor","SELinux and AppArmor",[14,744,745,748],{},[38,746,747],{},"Mandatory Access Control (MAC)"," systems that confine processes to what they're allowed to do, even if the process runs as root.",[77,750,752],{"id":751},"selinux-rhelfedoracentos","SELinux (RHEL\u002FFedora\u002FCentOS)",[754,755,756,769],"table",{},[757,758,759],"thead",{},[760,761,762,766],"tr",{},[763,764,765],"th",{},"Mode",[763,767,768],{},"Behavior",[770,771,772,783,793],"tbody",{},[760,773,774,780],{},[775,776,777],"td",{},[18,778,779],{},"enforcing",[775,781,782],{},"Policy enforced (violations blocked + logged)",[760,784,785,790],{},[775,786,787],{},[18,788,789],{},"permissive",[775,791,792],{},"Violations logged but not blocked (for testing)",[760,794,795,800],{},[775,796,797],{},[18,798,799],{},"disabled",[775,801,802],{},"Off",[82,804,805],{"language":84},[86,806,808],{"className":88,"code":807,"language":84,"meta":90,"style":90},"getenforce                      # current mode\nsudo setenforce 0               # switch to Permissive (temporary)\nsudo setenforce 1               # switch to Enforcing (temporary)\n\n# When SELinux blocks something (\"Permission denied\" but perms look fine):\nsudo ausearch -m AVC -ts recent # check denials\nrestorecon -Rv \u002Fvar\u002Fwww\u002Fhtml\u002F   # restore labels to policy defaults\naudit2allow -a                  # generate policy from audit log (denials → rules)\n\n# Anti-Pattern: disabling SELinux instead of fixing the policy\n# Don't disable — use permissive to debug, then fix the policy\n",[18,809,810,818,831,843,847,852,874,888,899,903,908],{"__ignoreMap":90},[94,811,812,815],{"class":96,"line":97},[94,813,814],{"class":107},"getenforce",[94,816,817],{"class":100},"                      # current mode\n",[94,819,820,822,825,828],{"class":96,"line":104},[94,821,108],{"class":107},[94,823,824],{"class":111}," setenforce",[94,826,827],{"class":223}," 0",[94,829,830],{"class":100},"               # switch to Permissive (temporary)\n",[94,832,833,835,837,840],{"class":96,"line":124},[94,834,108],{"class":107},[94,836,824],{"class":111},[94,838,839],{"class":223}," 1",[94,841,842],{"class":100},"               # switch to Enforcing (temporary)\n",[94,844,845],{"class":96,"line":139},[94,846,520],{"emptyLinePlaceholder":519},[94,848,849],{"class":96,"line":154},[94,850,851],{"class":100},"# When SELinux blocks something (\"Permission denied\" but perms look fine):\n",[94,853,854,856,859,862,865,868,871],{"class":96,"line":169},[94,855,108],{"class":107},[94,857,858],{"class":111}," ausearch",[94,860,861],{"class":223}," -m",[94,863,864],{"class":111}," AVC",[94,866,867],{"class":223}," -ts",[94,869,870],{"class":111}," recent",[94,872,873],{"class":100}," # check denials\n",[94,875,876,879,882,885],{"class":96,"line":184},[94,877,878],{"class":107},"restorecon",[94,880,881],{"class":223}," -Rv",[94,883,884],{"class":111}," \u002Fvar\u002Fwww\u002Fhtml\u002F",[94,886,887],{"class":100},"   # restore labels to policy defaults\n",[94,889,890,893,896],{"class":96,"line":199},[94,891,892],{"class":107},"audit2allow",[94,894,895],{"class":223}," -a",[94,897,898],{"class":100},"                  # generate policy from audit log (denials → rules)\n",[94,900,901],{"class":96,"line":230},[94,902,520],{"emptyLinePlaceholder":519},[94,904,905],{"class":96,"line":243},[94,906,907],{"class":100},"# Anti-Pattern: disabling SELinux instead of fixing the policy\n",[94,909,910],{"class":96,"line":660},[94,911,912],{"class":100},"# Don't disable — use permissive to debug, then fix the policy\n",[77,914,916],{"id":915},"apparmor-ubuntudebiansuse","AppArmor (Ubuntu\u002FDebian\u002FSUSE)",[82,918,919],{"language":84},[86,920,922],{"className":88,"code":921,"language":84,"meta":90,"style":90},"sudo apparmor_status             # status (profiles loaded, enforced)\nsudo aa-complain \u002Fetc\u002Fapparmor.d\u002Fusr.sbin.nginx  # log only (don't block)\nsudo aa-enforce \u002Fetc\u002Fapparmor.d\u002Fusr.sbin.nginx  # enforce\n",[18,923,924,934,947],{"__ignoreMap":90},[94,925,926,928,931],{"class":96,"line":97},[94,927,108],{"class":107},[94,929,930],{"class":111}," apparmor_status",[94,932,933],{"class":100},"             # status (profiles loaded, enforced)\n",[94,935,936,938,941,944],{"class":96,"line":104},[94,937,108],{"class":107},[94,939,940],{"class":111}," aa-complain",[94,942,943],{"class":111}," \u002Fetc\u002Fapparmor.d\u002Fusr.sbin.nginx",[94,945,946],{"class":100},"  # log only (don't block)\n",[94,948,949,951,954,956],{"class":96,"line":124},[94,950,108],{"class":107},[94,952,953],{"class":111}," aa-enforce",[94,955,943],{"class":111},[94,957,958],{"class":100},"  # enforce\n",[27,960,962],{"id":961},"capabilities","Capabilities",[14,964,965,966,968],{},"Traditional Unix has a binary privilege model: UID 0 (root) can do anything, other UIDs can't. ",[38,967,962],{}," split root's privileges into ~40 fine-grained rights.",[82,970,971],{"language":84},[86,972,974],{"className":88,"code":973,"language":84,"meta":90,"style":90},"# Complex Implementation: grant specific capability instead of setuid root\n# Allow a binary to bind to port 80 without root\nsudo setcap 'cap_net_bind_service+ep' \u002Fusr\u002Fbin\u002Fmyapp\ngetcap \u002Fusr\u002Fbin\u002Fmyapp                    # verify\n\n# Anti-Pattern: setuid root for the same purpose\n# sudo chmod u+s \u002Fusr\u002Fbin\u002Fmyapp → grants ALL root privileges (dangerous)\n\n# View process capabilities:\ncat \u002Fproc\u002F$$\u002Fstatus | grep Cap\ncapsh --decode=000001ffffffffff   # decode hex to names\n",[18,975,976,981,986,999,1010,1014,1019,1024,1028,1033,1056],{"__ignoreMap":90},[94,977,978],{"class":96,"line":97},[94,979,980],{"class":100},"# Complex Implementation: grant specific capability instead of setuid root\n",[94,982,983],{"class":96,"line":104},[94,984,985],{"class":100},"# Allow a binary to bind to port 80 without root\n",[94,987,988,990,993,996],{"class":96,"line":124},[94,989,108],{"class":107},[94,991,992],{"class":111}," setcap",[94,994,995],{"class":111}," 'cap_net_bind_service+ep'",[94,997,998],{"class":111}," \u002Fusr\u002Fbin\u002Fmyapp\n",[94,1000,1001,1004,1007],{"class":96,"line":139},[94,1002,1003],{"class":107},"getcap",[94,1005,1006],{"class":111}," \u002Fusr\u002Fbin\u002Fmyapp",[94,1008,1009],{"class":100},"                    # verify\n",[94,1011,1012],{"class":96,"line":154},[94,1013,520],{"emptyLinePlaceholder":519},[94,1015,1016],{"class":96,"line":169},[94,1017,1018],{"class":100},"# Anti-Pattern: setuid root for the same purpose\n",[94,1020,1021],{"class":96,"line":184},[94,1022,1023],{"class":100},"# sudo chmod u+s \u002Fusr\u002Fbin\u002Fmyapp → grants ALL root privileges (dangerous)\n",[94,1025,1026],{"class":96,"line":199},[94,1027,520],{"emptyLinePlaceholder":519},[94,1029,1030],{"class":96,"line":230},[94,1031,1032],{"class":100},"# View process capabilities:\n",[94,1034,1035,1038,1041,1044,1047,1050,1053],{"class":96,"line":243},[94,1036,1037],{"class":107},"cat",[94,1039,1040],{"class":111}," \u002Fproc\u002F",[94,1042,1043],{"class":223},"$$",[94,1045,1046],{"class":111},"\u002Fstatus",[94,1048,1049],{"class":619}," |",[94,1051,1052],{"class":107}," grep",[94,1054,1055],{"class":111}," Cap\n",[94,1057,1058,1061,1064],{"class":96,"line":660},[94,1059,1060],{"class":107},"capsh",[94,1062,1063],{"class":223}," --decode=000001ffffffffff",[94,1065,1066],{"class":100},"   # decode hex to names\n",[27,1068,1070,1072],{"id":1069},"auditd-security-auditing",[18,1071,24],{}," — Security Auditing",[82,1074,1075],{"language":84},[86,1076,1078],{"className":88,"code":1077,"language":84,"meta":90,"style":90},"# Complex Implementation: audit file changes + command execution\nsudo auditctl -w \u002Fetc\u002Fpasswd -p wa -k identity_changes\nsudo auditctl -w \u002Fetc\u002F -p wa -k config_changes\nsudo auditctl -a always,exit -F arch=b64 -S execve    # log every command run\n\n# Make rules persistent: add to \u002Fetc\u002Faudit\u002Frules.d\u002Faudit.rules\n\n# Query:\nsudo ausearch -k identity_changes       # search by key\nsudo ausearch -f \u002Fetc\u002Fpasswd            # by file\nsudo aureport --auth                    # authentication events\nsudo aureport --failed                  # failed events\n",[18,1079,1080,1085,1110,1130,1156,1160,1165,1169,1174,1188,1202,1215],{"__ignoreMap":90},[94,1081,1082],{"class":96,"line":97},[94,1083,1084],{"class":100},"# Complex Implementation: audit file changes + command execution\n",[94,1086,1087,1089,1092,1095,1098,1101,1104,1107],{"class":96,"line":104},[94,1088,108],{"class":107},[94,1090,1091],{"class":111}," auditctl",[94,1093,1094],{"class":223}," -w",[94,1096,1097],{"class":111}," \u002Fetc\u002Fpasswd",[94,1099,1100],{"class":223}," -p",[94,1102,1103],{"class":111}," wa",[94,1105,1106],{"class":223}," -k",[94,1108,1109],{"class":111}," identity_changes\n",[94,1111,1112,1114,1116,1118,1121,1123,1125,1127],{"class":96,"line":124},[94,1113,108],{"class":107},[94,1115,1091],{"class":111},[94,1117,1094],{"class":223},[94,1119,1120],{"class":111}," \u002Fetc\u002F",[94,1122,1100],{"class":223},[94,1124,1103],{"class":111},[94,1126,1106],{"class":223},[94,1128,1129],{"class":111}," config_changes\n",[94,1131,1132,1134,1136,1138,1141,1144,1147,1150,1153],{"class":96,"line":139},[94,1133,108],{"class":107},[94,1135,1091],{"class":111},[94,1137,895],{"class":223},[94,1139,1140],{"class":111}," always,exit",[94,1142,1143],{"class":223}," -F",[94,1145,1146],{"class":111}," arch=b64",[94,1148,1149],{"class":223}," -S",[94,1151,1152],{"class":111}," execve",[94,1154,1155],{"class":100},"    # log every command run\n",[94,1157,1158],{"class":96,"line":154},[94,1159,520],{"emptyLinePlaceholder":519},[94,1161,1162],{"class":96,"line":169},[94,1163,1164],{"class":100},"# Make rules persistent: add to \u002Fetc\u002Faudit\u002Frules.d\u002Faudit.rules\n",[94,1166,1167],{"class":96,"line":184},[94,1168,520],{"emptyLinePlaceholder":519},[94,1170,1171],{"class":96,"line":199},[94,1172,1173],{"class":100},"# Query:\n",[94,1175,1176,1178,1180,1182,1185],{"class":96,"line":230},[94,1177,108],{"class":107},[94,1179,858],{"class":111},[94,1181,1106],{"class":223},[94,1183,1184],{"class":111}," identity_changes",[94,1186,1187],{"class":100},"       # search by key\n",[94,1189,1190,1192,1194,1197,1199],{"class":96,"line":243},[94,1191,108],{"class":107},[94,1193,858],{"class":111},[94,1195,1196],{"class":223}," -f",[94,1198,1097],{"class":111},[94,1200,1201],{"class":100},"            # by file\n",[94,1203,1204,1206,1209,1212],{"class":96,"line":660},[94,1205,108],{"class":107},[94,1207,1208],{"class":111}," aureport",[94,1210,1211],{"class":223}," --auth",[94,1213,1214],{"class":100},"                    # authentication events\n",[94,1216,1217,1219,1221,1224],{"class":96,"line":666},[94,1218,108],{"class":107},[94,1220,1208],{"class":111},[94,1222,1223],{"class":223}," --failed",[94,1225,1226],{"class":100},"                  # failed events\n",[27,1228,1230],{"id":1229},"file-integrity-monitoring-aide","File Integrity Monitoring — AIDE",[82,1232,1233],{"language":84},[86,1234,1236],{"className":88,"code":1235,"language":84,"meta":90,"style":90},"# Complex Implementation: detect unauthorized file changes\nsudo apt install aide\nsudo aideinit                 # initialize the database (first run)\nsudo cp \u002Fvar\u002Flib\u002Faide\u002Faide.db.new \u002Fvar\u002Flib\u002Faide\u002Faide.db\nsudo aide --check             # compare current state to database\nsudo aide --update            # update after legitimate changes\n# Run aide --check daily via systemd timer\n",[18,1237,1238,1243,1256,1266,1279,1292,1304],{"__ignoreMap":90},[94,1239,1240],{"class":96,"line":97},[94,1241,1242],{"class":100},"# Complex Implementation: detect unauthorized file changes\n",[94,1244,1245,1247,1250,1253],{"class":96,"line":104},[94,1246,108],{"class":107},[94,1248,1249],{"class":111}," apt",[94,1251,1252],{"class":111}," install",[94,1254,1255],{"class":111}," aide\n",[94,1257,1258,1260,1263],{"class":96,"line":124},[94,1259,108],{"class":107},[94,1261,1262],{"class":111}," aideinit",[94,1264,1265],{"class":100},"                 # initialize the database (first run)\n",[94,1267,1268,1270,1273,1276],{"class":96,"line":139},[94,1269,108],{"class":107},[94,1271,1272],{"class":111}," cp",[94,1274,1275],{"class":111}," \u002Fvar\u002Flib\u002Faide\u002Faide.db.new",[94,1277,1278],{"class":111}," \u002Fvar\u002Flib\u002Faide\u002Faide.db\n",[94,1280,1281,1283,1286,1289],{"class":96,"line":154},[94,1282,108],{"class":107},[94,1284,1285],{"class":111}," aide",[94,1287,1288],{"class":223}," --check",[94,1290,1291],{"class":100},"             # compare current state to database\n",[94,1293,1294,1296,1298,1301],{"class":96,"line":169},[94,1295,108],{"class":107},[94,1297,1285],{"class":111},[94,1299,1300],{"class":223}," --update",[94,1302,1303],{"class":100},"            # update after legitimate changes\n",[94,1305,1306],{"class":96,"line":184},[94,1307,1308],{"class":100},"# Run aide --check daily via systemd timer\n",[27,1310,1312],{"id":1311},"network-hardening","Network Hardening",[82,1314,1315],{"language":84},[86,1316,1318],{"className":88,"code":1317,"language":84,"meta":90,"style":90},"# \u002Fetc\u002Fsysctl.d\u002F99-network-hardening.conf\nnet.ipv4.ip_forward = 0\nnet.ipv4.conf.all.accept_redirects = 0\nnet.ipv4.conf.all.accept_source_route = 0\nnet.ipv4.conf.all.rp_filter = 1\nnet.ipv4.icmp_echo_ignore_broadcasts = 1\nnet.ipv4.tcp_syncookies = 1\n",[18,1319,1320,1325,1336,1345,1354,1364,1373],{"__ignoreMap":90},[94,1321,1322],{"class":96,"line":97},[94,1323,1324],{"class":100},"# \u002Fetc\u002Fsysctl.d\u002F99-network-hardening.conf\n",[94,1326,1327,1330,1333],{"class":96,"line":104},[94,1328,1329],{"class":107},"net.ipv4.ip_forward",[94,1331,1332],{"class":111}," =",[94,1334,1335],{"class":223}," 0\n",[94,1337,1338,1341,1343],{"class":96,"line":124},[94,1339,1340],{"class":107},"net.ipv4.conf.all.accept_redirects",[94,1342,1332],{"class":111},[94,1344,1335],{"class":223},[94,1346,1347,1350,1352],{"class":96,"line":139},[94,1348,1349],{"class":107},"net.ipv4.conf.all.accept_source_route",[94,1351,1332],{"class":111},[94,1353,1335],{"class":223},[94,1355,1356,1359,1361],{"class":96,"line":154},[94,1357,1358],{"class":107},"net.ipv4.conf.all.rp_filter",[94,1360,1332],{"class":111},[94,1362,1363],{"class":223}," 1\n",[94,1365,1366,1369,1371],{"class":96,"line":169},[94,1367,1368],{"class":107},"net.ipv4.icmp_echo_ignore_broadcasts",[94,1370,1332],{"class":111},[94,1372,1363],{"class":223},[94,1374,1375,1378,1380],{"class":96,"line":184},[94,1376,1377],{"class":107},"net.ipv4.tcp_syncookies",[94,1379,1332],{"class":111},[94,1381,1363],{"class":223},[14,1383,1384,1385,1388],{},"Apply: ",[18,1386,1387],{},"sudo sysctl --system",".",[27,1390,1392],{"id":1391},"automatic-security-updates","Automatic Security Updates",[82,1394,1395],{"language":84},[86,1396,1398],{"className":88,"code":1397,"language":84,"meta":90,"style":90},"# Debian\u002FUbuntu\nsudo apt install unattended-upgrades\nsudo dpkg-reconfigure -plow unattended-upgrades   # enable\n# Config: \u002Fetc\u002Fapt\u002Fapt.conf.d\u002F50unattended-upgrades (security only by default)\n\n# RHEL\nsudo dnf install dnf-automatic\nsudo systemctl enable --now dnf-automatic.timer\n",[18,1399,1400,1405,1416,1432,1437,1441,1446,1458],{"__ignoreMap":90},[94,1401,1402],{"class":96,"line":97},[94,1403,1404],{"class":100},"# Debian\u002FUbuntu\n",[94,1406,1407,1409,1411,1413],{"class":96,"line":104},[94,1408,108],{"class":107},[94,1410,1249],{"class":111},[94,1412,1252],{"class":111},[94,1414,1415],{"class":111}," unattended-upgrades\n",[94,1417,1418,1420,1423,1426,1429],{"class":96,"line":124},[94,1419,108],{"class":107},[94,1421,1422],{"class":111}," dpkg-reconfigure",[94,1424,1425],{"class":223}," -plow",[94,1427,1428],{"class":111}," unattended-upgrades",[94,1430,1431],{"class":100},"   # enable\n",[94,1433,1434],{"class":96,"line":139},[94,1435,1436],{"class":100},"# Config: \u002Fetc\u002Fapt\u002Fapt.conf.d\u002F50unattended-upgrades (security only by default)\n",[94,1438,1439],{"class":96,"line":154},[94,1440,520],{"emptyLinePlaceholder":519},[94,1442,1443],{"class":96,"line":169},[94,1444,1445],{"class":100},"# RHEL\n",[94,1447,1448,1450,1453,1455],{"class":96,"line":184},[94,1449,108],{"class":107},[94,1451,1452],{"class":111}," dnf",[94,1454,1252],{"class":111},[94,1456,1457],{"class":111}," dnf-automatic\n",[94,1459,1460,1462,1464,1467,1470],{"class":96,"line":199},[94,1461,108],{"class":107},[94,1463,480],{"class":111},[94,1465,1466],{"class":111}," enable",[94,1468,1469],{"class":223}," --now",[94,1471,1472],{"class":111}," dnf-automatic.timer\n",[27,1474,1476],{"id":1475},"resource-limits","Resource Limits",[82,1478,1479],{"language":84},[86,1480,1482],{"className":88,"code":1481,"language":84,"meta":90,"style":90},"# Per-shell (ulimit):\nulimit -n 65536               # max open files\nulimit -u 4096                # max processes\n\n# Persistent (\u002Fetc\u002Fsecurity\u002Flimits.conf):\n# *    soft  nofile  65536\n# *    hard  nofile  65536\n\n# For services (systemd unit):\n# [Service]\n# LimitNOFILE=65536\n# LimitNPROC=4096\n",[18,1483,1484,1489,1503,1516,1520,1525,1530,1535,1539,1544,1549,1554],{"__ignoreMap":90},[94,1485,1486],{"class":96,"line":97},[94,1487,1488],{"class":100},"# Per-shell (ulimit):\n",[94,1490,1491,1494,1497,1500],{"class":96,"line":104},[94,1492,1493],{"class":223},"ulimit",[94,1495,1496],{"class":223}," -n",[94,1498,1499],{"class":223}," 65536",[94,1501,1502],{"class":100},"               # max open files\n",[94,1504,1505,1507,1510,1513],{"class":96,"line":124},[94,1506,1493],{"class":223},[94,1508,1509],{"class":223}," -u",[94,1511,1512],{"class":223}," 4096",[94,1514,1515],{"class":100},"                # max processes\n",[94,1517,1518],{"class":96,"line":139},[94,1519,520],{"emptyLinePlaceholder":519},[94,1521,1522],{"class":96,"line":154},[94,1523,1524],{"class":100},"# Persistent (\u002Fetc\u002Fsecurity\u002Flimits.conf):\n",[94,1526,1527],{"class":96,"line":169},[94,1528,1529],{"class":100},"# *    soft  nofile  65536\n",[94,1531,1532],{"class":96,"line":184},[94,1533,1534],{"class":100},"# *    hard  nofile  65536\n",[94,1536,1537],{"class":96,"line":199},[94,1538,520],{"emptyLinePlaceholder":519},[94,1540,1541],{"class":96,"line":230},[94,1542,1543],{"class":100},"# For services (systemd unit):\n",[94,1545,1546],{"class":96,"line":243},[94,1547,1548],{"class":100},"# [Service]\n",[94,1550,1551],{"class":96,"line":660},[94,1552,1553],{"class":100},"# LimitNOFILE=65536\n",[94,1555,1556],{"class":96,"line":666},[94,1557,1558],{"class":100},"# LimitNPROC=4096\n",[27,1560,1562],{"id":1561},"tips-tricks","💡 Tips & Tricks",[1564,1565,1566,1594,1612,1631,1645,1658],"ul",{},[35,1567,1568,1571,1572,1575,1576,1579,1580,1582,1583,1586,1587,1590,1591,1388],{},[38,1569,1570],{},"Idiom",": use ",[18,1573,1574],{},"visudo"," (never edit ",[18,1577,1578],{},"\u002Fetc\u002Fsudoers"," directly) — ",[18,1581,1574],{}," checks syntax before saving. A syntax error in ",[18,1584,1585],{},"sudoers"," can lock you out of root. Use ",[18,1588,1589],{},"\u002Fetc\u002Fsudoers.d\u002F"," files, mode ",[18,1592,1593],{},"440",[35,1595,1596,1598,1599,1602,1603,1606,1607,1611],{},[38,1597,1570],{},": prefer file capabilities over setuid root — ",[18,1600,1601],{},"setcap cap_net_bind_service+ep .\u002Fserver"," lets a binary bind to port 80 without running as root. Safer than ",[18,1604,1605],{},"chmod u+s"," (which grants ",[1608,1609,1610],"em",{},"all"," privileges).",[35,1613,1614,1571,1616,1618,1619,1622,1623,1626,1627,1630],{},[38,1615,1570],{},[18,1617,20],{}," with a custom ",[18,1620,1621],{},"jail.local"," (not ",[18,1624,1625],{},"jail.conf"," — it's overwritten on updates). Set ",[18,1628,1629],{},"banaction = ufw"," to integrate with your firewall.",[35,1632,1633,1635,1636,1638,1639,1641,1642,1644],{},[38,1634,1570],{},": don't disable SELinux — use ",[18,1637,789],{}," to debug, then fix the policy with ",[18,1640,892],{}," or ",[18,1643,878],{},". Disabling removes a key security layer.",[35,1646,1647,1649,1650,1653,1654,1657],{},[38,1648,1570],{},": install ",[18,1651,1652],{},"aide"," and establish a baseline at provisioning — run ",[18,1655,1656],{},"aide --check"," daily. An attacker who modifies binaries or configs will show up as a change.",[35,1659,1660,1571,1663,1666],{},[38,1661,1662],{},"Debug",[18,1664,1665],{},"sudo ausearch -m AVC -ts recent"," when SELinux blocks something — shows the exact denial and which process\u002Ffile was involved.",[27,1668,1670],{"id":1669},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[1564,1672,1673,1686,1696,1714,1728,1738,1760,1770],{},[35,1674,1675,1678,1679,1682,1683,1388],{},[38,1676,1677],{},"Always allow SSH before enabling UFW",": otherwise you lock yourself out. ",[18,1680,1681],{},"sudo ufw allow 22\u002Ftcp"," BEFORE ",[18,1684,1685],{},"sudo ufw enable",[35,1687,1688,1691,1692,1695],{},[38,1689,1690],{},"Never disable password auth before verifying key auth",": ",[18,1693,1694],{},"ssh -i key user@server"," first. If it works, THEN disable passwords.",[35,1697,1698,1707,1708,1641,1711,1713],{},[38,1699,1700,1703,1704],{},[18,1701,1702],{},"PermitRootLogin no"," doesn't lock out ",[18,1705,1706],{},"su -",": it only disables SSH login as root. You can still ",[18,1709,1710],{},"sudo -i",[18,1712,1706],{}," from a regular account.",[35,1715,1716,1719,1720,1723,1724,1727],{},[38,1717,1718],{},"SELinux blocks with \"Permission denied\" even when Unix perms look fine",": check ",[18,1721,1722],{},"ausearch -m AVC"," for denials. Common on RHEL when serving files with wrong labels (",[18,1725,1726],{},"restorecon -Rv"," fixes).",[35,1729,1730,1737],{},[38,1731,1732,1733,1736],{},"Passwordless sudo (",[18,1734,1735],{},"NOPASSWD: ALL",") is a security hole",": any compromise of that user = instant root. Limit to specific commands.",[35,1739,1740,1691,1745,1747,1748,1751,1752,1755,1756,1759],{},[38,1741,1742,1744],{},[18,1743,108],{}," timestamp is per-terminal by default",[18,1746,108],{}," in one terminal doesn't cache for another. Use ",[18,1749,1750],{},"sudo -v"," in each terminal, or set ",[18,1753,1754],{},"tty_tickets"," to ",[18,1757,1758],{},"no"," in sudoers (less secure).",[35,1761,1762,1765,1766,1769],{},[38,1763,1764],{},"Logs on a compromised machine can't be trusted",": an attacker with root can delete ",[18,1767,1768],{},"\u002Fvar\u002Flog",". Forward logs to a remote server (so an attacker can't delete them).",[35,1771,1772,1778],{},[38,1773,1774,1777],{},[18,1775,1776],{},"barrier=0"," risks data corruption on power loss",": write barriers ensure write ordering. Disabling them is faster but risks filesystem corruption.",[27,1780,1782],{"id":1781},"quick-quiz","🧠 Quick Quiz",[14,1784,1785,1786,1789,1790,1793],{},"An admin sets ",[18,1787,1788],{},"PasswordAuthentication no"," in ",[18,1791,1792],{},"sshd_config",", reloads sshd, and loses SSH access. They have no physical access to the server. What went wrong, and how could they have prevented this?",[1795,1796,1797,1801,1811,1816],"details",{},[1798,1799,1800],"summary",{},"Answer",[14,1802,1803,1804,21,1807,1810],{},"They disabled password authentication without verifying that key-based authentication works. Their SSH key may not be properly set up on the server (wrong permissions in ",[18,1805,1806],{},"~\u002F.ssh\u002F",[18,1808,1809],{},"authorized_keys"," not in place, or the key wasn't copied).",[14,1812,1813],{},[38,1814,1815],{},"Prevention:",[32,1817,1818,1849,1855,1880],{},[35,1819,1820,1823],{},[38,1821,1822],{},"Always test key auth before disabling passwords:",[82,1824,1825,1831,1835,1838,1842],{"language":84},[86,1826,1827],{"className":88,"code":90,"language":84,"meta":90,"style":90},[18,1828,1829],{"__ignoreMap":90},[94,1830],{"class":96,"line":97},[10,1832,1834],{"id":1833},"from-a-different-terminal-keep-the-current-ssh-session-open-as-a-safety-net","From a DIFFERENT terminal (keep the current SSH session open as a safety net):",[14,1836,1837],{},"ssh -i ~\u002F.ssh\u002Fid_ed25519 alice@server",[10,1839,1841],{"id":1840},"if-this-works-youre-safe-to-disable-passwords","If this works, you're safe to disable passwords",[86,1843,1847],{"className":1844,"code":90,"language":1846},[1845],"language-text","text",[18,1848,90],{"__ignoreMap":90},[35,1850,1851,1854],{},[38,1852,1853],{},"Keep your current SSH session open"," while making SSH config changes. If the new config breaks, your existing session is unaffected (you can fix the config from there).",[35,1856,1857,1863,1864],{},[38,1858,1859,1860],{},"Use ",[18,1861,1862],{},"sshd -t"," to test config syntax before reloading:",[82,1865,1866,1872,1875],{"language":84},[86,1867,1868],{"className":88,"code":90,"language":84,"meta":90,"style":90},[18,1869,1870],{"__ignoreMap":90},[94,1871],{"class":96,"line":97},[14,1873,1874],{},"sudo sshd -t                    # test syntax\nsudo systemctl reload sshd      # reload (not restart — no dropped connections)",[86,1876,1878],{"className":1877,"code":90,"language":1846},[1845],[18,1879,90],{"__ignoreMap":90},[35,1881,1882,1885,1886,1889],{},[38,1883,1884],{},"If already locked out",": boot to rescue mode (cloud provider's console, or VNC), mount the root filesystem, and re-enable ",[18,1887,1888],{},"PasswordAuthentication yes"," or fix the key setup.",[1891,1892,1893],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}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);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}",{"title":90,"searchDepth":104,"depth":104,"links":1895},[1896,1897,1902,1905,1907,1911,1912,1914,1915,1916,1917,1918,1919,1920],{"id":29,"depth":104,"text":30},{"id":74,"depth":104,"text":75,"children":1898},[1899,1900,1901],{"id":79,"depth":124,"text":80},{"id":256,"depth":124,"text":257},{"id":304,"depth":124,"text":305},{"id":365,"depth":104,"text":366,"children":1903},[1904],{"id":491,"depth":124,"text":492},{"id":586,"depth":104,"text":1906},"fail2ban — Brute-Force Protection",{"id":741,"depth":104,"text":742,"children":1908},[1909,1910],{"id":751,"depth":124,"text":752},{"id":915,"depth":124,"text":916},{"id":961,"depth":104,"text":962},{"id":1069,"depth":104,"text":1913},"auditd — Security Auditing",{"id":1229,"depth":104,"text":1230},{"id":1311,"depth":104,"text":1312},{"id":1391,"depth":104,"text":1392},{"id":1475,"depth":104,"text":1476},{"id":1561,"depth":104,"text":1562},{"id":1669,"depth":104,"text":1670},{"id":1781,"depth":104,"text":1782},"Linux is relatively secure by default, but a stock install is not hardened. This chapter covers the defense-in-depth stack: firewall, SSH hardening, SELinux\u002FAppArmor, fail2ban, auditd, capabilities, and the mindset of reducing attack surface. Security is layered — no single tool is sufficient.","md",{},"\u002Flinux\u002F14-security-and-hardening",{"title":5,"description":1921},"linux\u002F14-security-and-hardening","cE5K2NoovE27GE-gdeYKLJy68gnUPl-Vc-GokRpbMv8",1789924650108]