[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6X7SERMJGVR2JFJ66SDQ48R":3,"skill-same:01M6X7SERMJGVR2JFJ66SDQ48R":83},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":57,"repositoryHtmlUrl":58,"repositoryHomepageUrl":59,"ownerHtmlUrl":60,"ownerBlogUrl":59,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6X7SERMJGVR2JFJ66SDQ48R","server-hardening",null,"Use when hardening a Linux host you operate — SSH posture, a default-deny firewall baseline, and unattended security upgrades, each verified on the box rather than assumed from a config file.","event4u-app\u002Fagent-config","event4u-app","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F199339632?v=4","01M6X5TWBNCQCE1VDKT4E9KF7N","event4u.app","01M6X5TWF4HRM266RG2Q5VVH1X",19,"MIT","https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Fsrc\u002Fskills\u002Fserver-hardening\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"server","hardening","server hardening","use","when","unattended","verified","operate","you","host","default","devops","linux","upgrades","ssh","baseline","firewall","file","config","security","2026-11-10 10:12:30",false,"---\nmodel_tier: medium\nname: server-hardening\ndescription: \"Use when hardening a Linux host you operate — SSH posture, a default-deny firewall baseline, and unattended security upgrades, each verified on the box rather than assumed from a config file.\"\ndomain: devops\nworkspaces:\n  - engineering\npacks:\n  - engineering-base\n---\n\n# server-hardening\n\n## When to use\n\nUse when a host is yours to operate — a VPS, a bare-metal box, a long-lived VM —\nand its own security posture is the question. Three surfaces: who may log in, what\nmay reach the network, and how patches arrive.\n\nDo NOT use when:\n- The workload runs on a managed runtime with no host you control — there is no\n  host posture to set, and `operational-readiness` records that as `unavailable`\n  with that reason\n- Auditing application code for vulnerabilities (use `security-audit`)\n- Application-level auth and authorization (use `security`, `authz-review`)\n- Provisioning cloud infrastructure (use `terraform`, `aws-infrastructure`)\n- Container image hardening (use `docker`)\n\n## The three surfaces\n\n### 1. SSH posture\n\nThe one remote-entry door. Harden in this order, and **keep an open session while\nyou change it** — a mistake here locks you out of the machine you are fixing.\n\n| Setting | Target | Why |\n|---|---|---|\n| `PasswordAuthentication` | `no` | Ends credential-stuffing against the host outright |\n| `PubkeyAuthentication` | `yes` | The replacement must work before passwords are removed |\n| `PermitRootLogin` | `no` (or `prohibit-password`) | Forces an audit trail through a named account |\n| `AllowUsers` \u002F `AllowGroups` | Explicit allow-list | Default is every account on the box |\n| Port | Non-default is optional | Cuts log noise, not risk — never the primary control |\n\nVerify from a **second** connection before closing the first:\n\n```bash\nsudo sshd -t                                  # config parses; do this before reload\nsudo systemctl reload ssh                     # or sshd, per distro\nsudo sshd -T | grep -Ei 'passwordauth|permitrootlogin|pubkeyauth'\n```\n\n`sshd -T` prints the **effective** configuration. A file under\n`sshd_config.d\u002F` or a `Match` block can override what you just edited, so the\nfile you wrote is not evidence — the effective dump is.\n\n### 2. Firewall baseline\n\nDefault-deny inbound, allow-list outbound where the workload permits.\n\n```bash\nsudo ufw default deny incoming\nsudo ufw default allow outgoing\nsudo ufw allow OpenSSH                        # before enabling, or you are locked out\nsudo ufw enable\nsudo ufw status verbose                       # the verification, not the intent\n```\n\nTwo traps worth naming. A container runtime can write its own forwarding rules\nthat bypass the host firewall entirely — check the actual rule table\n(`sudo iptables -S` \u002F `sudo nft list ruleset`), not just the friendly front-end.\nAnd a cloud security group sits *in front* of the host: both must agree, and the\nnarrower one is the effective policy.\n\nOptionally add an SSH brute-force throttle (`fail2ban` or equivalent). It is\nnoise reduction and a small real gain once password auth is already off — never\na substitute for it.\n\n### 3. Unattended security upgrades\n\nPatches that require a human are patches that arrive late.\n\n```bash\nsudo apt install unattended-upgrades          # Debian\u002FUbuntu family\nsudo dpkg-reconfigure -plow unattended-upgrades\nsudo unattended-upgrade --dry-run --debug     # proves it would act\n```\n\nDecide and record two things, because the default is silence: whether the host\n**reboots automatically** when a patch needs it, and where the **failure\nnotification** goes. An upgrade timer that has been failing for six weeks is\nindistinguishable from one that is working, unless something reports it.\n\nSecurity patches only. Automatic *feature* upgrades change behaviour under you,\nwhich is a different risk with a different owner.\n\n## Procedure: Harden a host\n\n1. **Confirm the host is yours to harden.** Managed runtime with no shell → stop;\n   there is no posture here, and that is a recordable `unavailable`.\n2. **Open a second session and keep it open** for the whole SSH and firewall\n   work. This is the entire rollback mechanism.\n3. **Set the SSH posture**, then verify with `sshd -T` from the second session —\n   effective config, not the file.\n4. **Set the firewall baseline**, allowing SSH *before* enabling. Verify with\n   `ufw status verbose` and against the raw rule table.\n5. **Enable unattended security upgrades**, then prove with `--dry-run` that it\n   would act. Record the reboot policy and the failure destination.\n6. **Re-verify from a fresh connection.** Close every old session and reconnect.\n   A posture that only holds inside an already-authenticated session is\n   unverified.\n7. **Report each surface as met, unmet or uninspected.** Uninspected is not met —\n   `operational-readiness` reads an uninspected host posture as a red.\n\n### Validate\n\n- Verify `sshd -T` reports `passwordauthentication no`.\n- Verify `sshd -T` reports `permitrootlogin no` or `prohibit-password`.\n- Verify a key-based login succeeds from a **fresh** connection.\n- Verify the firewall default is deny-inbound and SSH is explicitly allowed.\n- Verify the raw rule table agrees with the front-end.\n- Verify `unattended-upgrade --dry-run` reports it would act.\n- Confirm the reboot policy and the failure-notification destination are recorded.\n\n## Output format\n\n1. One row per surface — SSH, firewall, unattended upgrades — with met \u002F unmet \u002F\n   uninspected and the verifying command's output.\n2. The reboot policy and where upgrade failures are reported.\n3. Anything left unmet, with what would close it.\n\n## Gotcha\n\n- Changing SSH without a second open session is how a host becomes unreachable.\n- The config file is not the posture. `sshd -T` is; a drop-in or `Match` block\n  silently wins.\n- Enabling a firewall before allowing SSH locks you out immediately.\n- A container runtime's own rules can bypass the host firewall.\n- A cloud security group and the host firewall must both allow a port; the\n  narrower wins.\n- A silently failing upgrade timer looks exactly like a working one.\n- A non-default SSH port reduces log volume, not risk.\n\n## Do NOT\n\n- Do NOT disable password authentication before a key login is proven to work.\n- Do NOT enable the firewall before allowing SSH.\n- Do NOT enable automatic feature upgrades — security patches only.\n- Do NOT treat an installed package as an enabled control; prove it acts.\n- Do NOT report an uninspected surface as met.\n- Do NOT rely on a non-default port as a control.\n\n## Auto-trigger keywords\n\n- server hardening\n- host hardening\n- ssh hardening\n- firewall baseline\n- unattended upgrades\n- fail2ban\n","# server-hardening\n\n## When to use\n\nUse when a host is yours to operate — a VPS, a bare-metal box, a long-lived VM —\nand its own security posture is the question. Three surfaces: who may log in, what\nmay reach the network, and how patches arrive.\n\nDo NOT use when:\n- The workload runs on a managed runtime with no host you control — there is no\n  host posture to set, and `operational-readiness` records that as `unavailable`\n  with that reason\n- Auditing application code for vulnerabilities (use `security-audit`)\n- Application-level auth and authorization (use `security`, `authz-review`)\n- Provisioning cloud infrastructure (use `terraform`, `aws-infrastructure`)\n- Container image hardening (use `docker`)\n\n## The three surfaces\n\n### 1. SSH posture\n\nThe one remote-entry door. Harden in this order, and **keep an open session while\nyou change it** — a mistake here locks you out of the machine you are fixing.\n\n| Setting | Target | Why |\n|---|---|---|\n| `PasswordAuthentication` | `no` | Ends credential-stuffing against the host outright |\n| `PubkeyAuthentication` | `yes` | The replacement must work before passwords are removed |\n| `PermitRootLogin` | `no` (or `prohibit-password`) | Forces an audit trail through a named account |\n| `AllowUsers` \u002F `AllowGroups` | Explicit allow-list | Default is every account on the box |\n| Port | Non-default is optional | Cuts log noise, not risk — never the primary control |\n\nVerify from a **second** connection before closing the first:\n\n```bash\nsudo sshd -t                                  # config parses; do this before reload\nsudo systemctl reload ssh                     # or sshd, per distro\nsudo sshd -T | grep -Ei 'passwordauth|permitrootlogin|pubkeyauth'\n```\n\n`sshd -T` prints the **effective** configuration. A file under\n`sshd_config.d\u002F` or a `Match` block can override what you just edited, so the\nfile you wrote is not evidence — the effective dump is.\n\n### 2. Firewall baseline\n\nDefault-deny inbound, allow-list outbound where the workload permits.\n\n```bash\nsudo ufw default deny incoming\nsudo ufw default allow outgoing\nsudo ufw allow OpenSSH                        # before enabling, or you are locked out\nsudo ufw enable\nsudo ufw status verbose                       # the verification, not the intent\n```\n\nTwo traps worth naming. A container runtime can write its own forwarding rules\nthat bypass the host firewall entirely — check the actual rule table\n(`sudo iptables -S` \u002F `sudo nft list ruleset`), not just the friendly front-end.\nAnd a cloud security group sits *in front* of the host: both must agree, and the\nnarrower one is the effective policy.\n\nOptionally add an SSH brute-force throttle (`fail2ban` or equivalent). It is\nnoise reduction and a small real gain once password auth is already off — never\na substitute for it.\n\n### 3. Unattended security upgrades\n\nPatches that require a human are patches that arrive late.\n\n```bash\nsudo apt install unattended-upgrades          # Debian\u002FUbuntu family\nsudo dpkg-reconfigure -plow unattended-upgrades\nsudo unattended-upgrade --dry-run --debug     # proves it would act\n```\n\nDecide and record two things, because the default is silence: whether the host\n**reboots automatically** when a patch needs it, and where the **failure\nnotification** goes. An upgrade timer that has been failing for six weeks is\nindistinguishable from one that is working, unless something reports it.\n\nSecurity patches only. Automatic *feature* upgrades change behaviour under you,\nwhich is a different risk with a different owner.\n\n## Procedure: Harden a host\n\n1. **Confirm the host is yours to harden.** Managed runtime with no shell → stop;\n   there is no posture here, and that is a recordable `unavailable`.\n2. **Open a second session and keep it open** for the whole SSH and firewall\n   work. This is the entire rollback mechanism.\n3. **Set the SSH posture**, then verify with `sshd -T` from the second session —\n   effective config, not the file.\n4. **Set the firewall baseline**, allowing SSH *before* enabling. Verify with\n   `ufw status verbose` and against the raw rule table.\n5. **Enable unattended security upgrades**, then prove with `--dry-run` that it\n   would act. Record the reboot policy and the failure destination.\n6. **Re-verify from a fresh connection.** Close every old session and reconnect.\n   A posture that only holds inside an already-authenticated session is\n   unverified.\n7. **Report each surface as met, unmet or uninspected.** Uninspected is not met —\n   `operational-readiness` reads an uninspected host posture as a red.\n\n### Validate\n\n- Verify `sshd -T` reports `passwordauthentication no`.\n- Verify `sshd -T` reports `permitrootlogin no` or `prohibit-password`.\n- Verify a key-based login succeeds from a **fresh** connection.\n- Verify the firewall default is deny-inbound and SSH is explicitly allowed.\n- Verify the raw rule table agrees with the front-end.\n- Verify `unattended-upgrade --dry-run` reports it would act.\n- Confirm the reboot policy and the failure-notification destination are recorded.\n\n## Output format\n\n1. One row per surface — SSH, firewall, unattended upgrades — with met \u002F unmet \u002F\n   uninspected and the verifying command's output.\n2. The reboot policy and where upgrade failures are reported.\n3. Anything left unmet, with what would close it.\n\n## Gotcha\n\n- Changing SSH without a second open session is how a host becomes unreachable.\n- The config file is not the posture. `sshd -T` is; a drop-in or `Match` block\n  silently wins.\n- Enabling a firewall before allowing SSH locks you out immediately.\n- A container runtime's own rules can bypass the host firewall.\n- A cloud security group and the host firewall must both allow a port; the\n  narrower wins.\n- A silently failing upgrade timer looks exactly like a working one.\n- A non-default SSH port reduces log volume, not risk.\n\n## Do NOT\n\n- Do NOT disable password authentication before a key login is proven to work.\n- Do NOT enable the firewall before allowing SSH.\n- Do NOT enable automatic feature upgrades — security patches only.\n- Do NOT treat an installed package as an enabled control; prove it acts.\n- Do NOT report an uninspected surface as met.\n- Do NOT rely on a non-default port as a control.\n\n## Auto-trigger keywords\n\n- server hardening\n- host hardening\n- ssh hardening\n- firewall baseline\n- unattended upgrades\n- fail2ban\n",{"name":5,"packs":49,"domain":35,"model_tier":51,"workspaces":52,"description":7},[50],"engineering-base","medium",[53],"engineering","main","12bd765ce1d800501d45dfb8232102793af01290","src\u002Fskills\u002Fserver-hardening",1,"https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config","https:\u002F\u002Fevent4u.app","https:\u002F\u002Fgithub.com\u002Fevent4u-app","Your events in your hand","2026-11-13 03:25:49",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add event4u-app\u002Fagent-config --skill server-hardening",{"tool":69,"command":70},"bunx","bunx skills add event4u-app\u002Fagent-config --skill server-hardening",{"tool":72,"command":73},"pnpm","pnpm dlx skills add event4u-app\u002Fagent-config --skill server-hardening","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Fsrc\u002Fskills\u002Fserver-hardening\u002FSKILL.md",true,[77],{"path":78,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":81,"sourceHtmlUrl":82},"evals\u002Ftriggers.json","REFERENCE","参考文档",1642,"https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Fsrc\u002Fskills\u002Fserver-hardening\u002Fevals\u002Ftriggers.json",[84,100,113,125,137,147],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":93,"publishedAt":44},"01M6X84PK96XBPENXH66JCTSD1","unknown-key-fixture","Declares a key the schema does not know.","https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Ftests\u002Fscripts\u002Ffixtures\u002Fbody-portable\u002Funknown-key\u002FSKILL.md",[90],{"categoryNo":91,"categoryName":92,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[94,95,96,97,98,99],"unknown key fixture","Unknown key","fixture","unknown","schema","package",{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":107,"publishedAt":44},"01M6X84HW7NAHZ7GZMQ9RXK1XJ","portable-fixture","Declares only semantics the MCP-lite carrier transports whole.","https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Ftests\u002Fscripts\u002Ffixtures\u002Fbody-portable\u002Fportable\u002FSKILL.md",[106],{"categoryNo":91,"categoryName":92,"taxonomyType":21,"taxonomyTypeDesc":22},[96,108,109,110,111,99,112],"portable","portable fixture","engineer","only","MCP",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":120,"publishedAt":44},"01M6X84F29FE0RGGZ1ZTFGZTX2","non-portable-fixture","Declares a runtime semantic the carrier drops.","https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Ftests\u002Fscripts\u002Ffixtures\u002Fbody-portable\u002Fnon-portable\u002FSKILL.md",[119],{"categoryNo":91,"categoryName":92,"taxonomyType":21,"taxonomyTypeDesc":22},[109,121,108,96,122,123,124,99],"non portable fixture","semantic","high","runtime",{"skillNo":126,"name":127,"nameZh":6,"description":128,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":132,"publishedAt":44},"01M6X848F706PT9MDCX75JKJ6B","candidate-zero-width","A candidate whose prose carries an invisible joiner.","https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskill-scout-candidates\u002Fzero-width-injection\u002FSKILL.md",[131],{"categoryNo":91,"categoryName":92,"taxonomyType":21,"taxonomyTypeDesc":22},[133,134,135,136],"candidate zero width","candidate","ZeRO","prose",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":144,"publishedAt":44},"01M6X841SSC2T31VDF41SA6DG1","candidate-disclosure-suppression","A candidate whose prose tells the agent to withhold from the user.","https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskill-scout-candidates\u002Fdisclosure-suppression\u002FSKILL.md",[143],{"categoryNo":91,"categoryName":92,"taxonomyType":21,"taxonomyTypeDesc":22},[145,134,136,146],"candidate disclosure suppression","user",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":154,"publishedAt":44},"01M6X83TFS5AGPBPVTQNWP0BB3","candidate-dangerous-frontmatter","A candidate claiming an unscoped tool grant and a permission bypass.","https:\u002F\u002Fgithub.com\u002Fevent4u-app\u002Fagent-config\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskill-scout-candidates\u002Fdangerous-frontmatter\u002FSKILL.md",[153],{"categoryNo":91,"categoryName":92,"taxonomyType":21,"taxonomyTypeDesc":22},[155,134,156,157,158,159],"candidate dangerous frontmatter","frontmatter","shell","permission","tool"]