[{"data":1,"prerenderedAt":1117},["ShallowReactive",2],{"page-en-\u002Fblog\u002Fssh-config-and-tunneling":3,"related-en-\u002Fblog\u002Fssh-config-and-tunneling":1095},{"id":4,"title":5,"body":6,"date":1082,"description":1083,"extension":1084,"meta":1085,"navigation":314,"path":1086,"seo":1087,"stem":1088,"tags":1089,"translation":1093,"__hash__":1094},"pages_en\u002Fen\u002Fblog\u002Fssh-config-and-tunneling.md","SSH Config, Key Management and Tunneling",{"type":7,"value":8,"toc":1065},"minimark",[9,14,27,31,82,92,95,113,117,136,142,177,181,184,235,241,245,248,399,413,441,445,448,467,470,526,532,536,541,548,573,583,586,608,612,615,639,643,646,678,740,744,822,828,832,841,875,908,914,921,925,1035,1046,1050,1061],[10,11,13],"h2",{"id":12},"introduction","Introduction",[15,16,17,18,22,23,26],"p",{},"For many of us SSH is just typing ",[19,20,21],"code",{},"ssh root@1.2.3.4"," and entering a password. A few minutes of setup gives you something far more secure and far faster: passwordless key login, shortcuts like ",[19,24,25],{},"ssh prod",", connecting through a bastion in one command, and reaching a remote database through a local port.",[10,28,30],{"id":29},"_1-generating-a-key","1. Generating a key",[32,33,38],"pre",{"className":34,"code":35,"language":36,"meta":37,"style":37},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","ssh-keygen -t ed25519 -C \"sukru@laptop\"\n# ~\u002F.ssh\u002Fid_ed25519      → private key (never shared)\n# ~\u002F.ssh\u002Fid_ed25519.pub  → public key (added to servers)\n","bash","",[19,39,40,69,76],{"__ignoreMap":37},[41,42,45,49,53,56,59,63,66],"span",{"class":43,"line":44},"line",1,[41,46,48],{"class":47},"sBMFI","ssh-keygen",[41,50,52],{"class":51},"sfazB"," -t",[41,54,55],{"class":51}," ed25519",[41,57,58],{"class":51}," -C",[41,60,62],{"class":61},"sMK4o"," \"",[41,64,65],{"class":51},"sukru@laptop",[41,67,68],{"class":61},"\"\n",[41,70,72],{"class":43,"line":71},2,[41,73,75],{"class":74},"sHwdD","# ~\u002F.ssh\u002Fid_ed25519      → private key (never shared)\n",[41,77,79],{"class":43,"line":78},3,[41,80,81],{"class":74},"# ~\u002F.ssh\u002Fid_ed25519.pub  → public key (added to servers)\n",[15,83,84,87,88,91],{},[19,85,86],{},"ed25519"," is shorter and faster than RSA and is the currently recommended algorithm. If an old system needs RSA, use at least 4096 bits: ",[19,89,90],{},"ssh-keygen -t rsa -b 4096",".",[15,93,94],{},"A passphrase keeps the key unusable even if the file is stolen. To add or change one later:",[32,96,98],{"className":34,"code":97,"language":36,"meta":37,"style":37},"ssh-keygen -p -f ~\u002F.ssh\u002Fid_ed25519\n",[19,99,100],{"__ignoreMap":37},[41,101,102,104,107,110],{"class":43,"line":44},[41,103,48],{"class":47},[41,105,106],{"class":51}," -p",[41,108,109],{"class":51}," -f",[41,111,112],{"class":51}," ~\u002F.ssh\u002Fid_ed25519\n",[10,114,116],{"id":115},"_2-adding-the-public-key-to-a-server","2. Adding the public key to a server",[32,118,120],{"className":34,"code":119,"language":36,"meta":37,"style":37},"ssh-copy-id -i ~\u002F.ssh\u002Fid_ed25519.pub deploy@203.0.113.10\n",[19,121,122],{"__ignoreMap":37},[41,123,124,127,130,133],{"class":43,"line":44},[41,125,126],{"class":47},"ssh-copy-id",[41,128,129],{"class":51}," -i",[41,131,132],{"class":51}," ~\u002F.ssh\u002Fid_ed25519.pub",[41,134,135],{"class":51}," deploy@203.0.113.10\n",[15,137,138,139,141],{},"Without ",[19,140,126],{}," (e.g. on Windows) you can do the same by hand:",[32,143,145],{"className":34,"code":144,"language":36,"meta":37,"style":37},"cat ~\u002F.ssh\u002Fid_ed25519.pub | ssh deploy@203.0.113.10 \\\n  \"mkdir -p ~\u002F.ssh && chmod 700 ~\u002F.ssh && cat >> ~\u002F.ssh\u002Fauthorized_keys && chmod 600 ~\u002F.ssh\u002Fauthorized_keys\"\n",[19,146,147,167],{"__ignoreMap":37},[41,148,149,152,154,157,160,163],{"class":43,"line":44},[41,150,151],{"class":47},"cat",[41,153,132],{"class":51},[41,155,156],{"class":61}," |",[41,158,159],{"class":47}," ssh",[41,161,162],{"class":51}," deploy@203.0.113.10",[41,164,166],{"class":165},"sTEyZ"," \\\n",[41,168,169,172,175],{"class":43,"line":71},[41,170,171],{"class":61},"  \"",[41,173,174],{"class":51},"mkdir -p ~\u002F.ssh && chmod 700 ~\u002F.ssh && cat >> ~\u002F.ssh\u002Fauthorized_keys && chmod 600 ~\u002F.ssh\u002Fauthorized_keys",[41,176,68],{"class":61},[10,178,180],{"id":179},"_3-ssh-agent","3. ssh-agent",[15,182,183],{},"To avoid typing the passphrase on every connection, add the key to the agent:",[32,185,187],{"className":34,"code":186,"language":36,"meta":37,"style":37},"eval \"$(ssh-agent -s)\"\nssh-add ~\u002F.ssh\u002Fid_ed25519\nssh-add -l                 # keys in the agent\nssh-add -D                 # remove all\n",[19,188,189,207,214,224],{"__ignoreMap":37},[41,190,191,195,198,201,204],{"class":43,"line":44},[41,192,194],{"class":193},"s2Zo4","eval",[41,196,197],{"class":61}," \"$(",[41,199,200],{"class":47},"ssh-agent",[41,202,203],{"class":51}," -s",[41,205,206],{"class":61},")\"\n",[41,208,209,212],{"class":43,"line":71},[41,210,211],{"class":47},"ssh-add",[41,213,112],{"class":51},[41,215,216,218,221],{"class":43,"line":78},[41,217,211],{"class":47},[41,219,220],{"class":51}," -l",[41,222,223],{"class":74},"                 # keys in the agent\n",[41,225,227,229,232],{"class":43,"line":226},4,[41,228,211],{"class":47},[41,230,231],{"class":51}," -D",[41,233,234],{"class":74},"                 # remove all\n",[15,236,237,238,91],{},"On macOS, store the passphrase in the Keychain with ",[19,239,240],{},"ssh-add --apple-use-keychain ~\u002F.ssh\u002Fid_ed25519",[10,242,244],{"id":243},"_4-shortcuts-with-sshconfig","4. Shortcuts with ~\u002F.ssh\u002Fconfig",[15,246,247],{},"Instead of typing user, IP, port and key every time:",[32,249,251],{"className":34,"code":250,"language":36,"meta":37,"style":37},"# ~\u002F.ssh\u002Fconfig\nHost prod\n    HostName 203.0.113.10\n    User deploy\n    Port 2222\n    IdentityFile ~\u002F.ssh\u002Fid_ed25519\n    IdentitiesOnly yes\n\nHost staging\n    HostName 203.0.113.20\n    User deploy\n\nHost github.com\n    IdentityFile ~\u002F.ssh\u002Fid_ed25519_github\n\nHost *\n    ServerAliveInterval 60\n    ServerAliveCountMax 3\n    AddKeysToAgent yes\n",[19,252,253,258,266,275,283,292,300,309,316,324,332,339,344,352,360,365,373,382,391],{"__ignoreMap":37},[41,254,255],{"class":43,"line":44},[41,256,257],{"class":74},"# ~\u002F.ssh\u002Fconfig\n",[41,259,260,263],{"class":43,"line":71},[41,261,262],{"class":47},"Host",[41,264,265],{"class":51}," prod\n",[41,267,268,271],{"class":43,"line":78},[41,269,270],{"class":47},"    HostName",[41,272,274],{"class":273},"sbssI"," 203.0.113.10\n",[41,276,277,280],{"class":43,"line":226},[41,278,279],{"class":47},"    User",[41,281,282],{"class":51}," deploy\n",[41,284,286,289],{"class":43,"line":285},5,[41,287,288],{"class":47},"    Port",[41,290,291],{"class":273}," 2222\n",[41,293,295,298],{"class":43,"line":294},6,[41,296,297],{"class":47},"    IdentityFile",[41,299,112],{"class":51},[41,301,303,306],{"class":43,"line":302},7,[41,304,305],{"class":47},"    IdentitiesOnly",[41,307,308],{"class":51}," yes\n",[41,310,312],{"class":43,"line":311},8,[41,313,315],{"emptyLinePlaceholder":314},true,"\n",[41,317,319,321],{"class":43,"line":318},9,[41,320,262],{"class":47},[41,322,323],{"class":51}," staging\n",[41,325,327,329],{"class":43,"line":326},10,[41,328,270],{"class":47},[41,330,331],{"class":273}," 203.0.113.20\n",[41,333,335,337],{"class":43,"line":334},11,[41,336,279],{"class":47},[41,338,282],{"class":51},[41,340,342],{"class":43,"line":341},12,[41,343,315],{"emptyLinePlaceholder":314},[41,345,347,349],{"class":43,"line":346},13,[41,348,262],{"class":47},[41,350,351],{"class":51}," github.com\n",[41,353,355,357],{"class":43,"line":354},14,[41,356,297],{"class":47},[41,358,359],{"class":51}," ~\u002F.ssh\u002Fid_ed25519_github\n",[41,361,363],{"class":43,"line":362},15,[41,364,315],{"emptyLinePlaceholder":314},[41,366,368,370],{"class":43,"line":367},16,[41,369,262],{"class":47},[41,371,372],{"class":165}," *\n",[41,374,376,379],{"class":43,"line":375},17,[41,377,378],{"class":47},"    ServerAliveInterval",[41,380,381],{"class":273}," 60\n",[41,383,385,388],{"class":43,"line":384},18,[41,386,387],{"class":47},"    ServerAliveCountMax",[41,389,390],{"class":273}," 3\n",[41,392,394,397],{"class":43,"line":393},19,[41,395,396],{"class":47},"    AddKeysToAgent",[41,398,308],{"class":51},[15,400,401,402,404,405,408,409,412],{},"Now ",[19,403,25],{},", ",[19,406,407],{},"scp app.jar prod:\u002Fopt\u002Fapp\u002F"," and ",[19,410,411],{},"rsync -az dist\u002F prod:\u002Fvar\u002Fwww\u002F"," just work.",[414,415,416,423,429],"ul",{},[417,418,419,422],"li",{},[19,420,421],{},"IdentitiesOnly yes"," uses only the given key instead of trying every key in the agent; it prevents \"Too many authentication failures\".",[417,424,425,428],{},[19,426,427],{},"ServerAliveInterval"," keeps idle connections from being dropped by firewalls\u002FNAT.",[417,430,431,432,436,437,440],{},"The ",[433,434,435],"strong",{},"first matching value"," wins, so put the ",[19,438,439],{},"Host *"," block at the end of the file.",[10,442,444],{"id":443},"_5-jump-host-bastion","5. Jump host (bastion)",[15,446,447],{},"When a server on a private network is reachable only through a bastion:",[32,449,451],{"className":34,"code":450,"language":36,"meta":37,"style":37},"ssh -J deploy@bastion.example.com deploy@10.0.1.15\n",[19,452,453],{"__ignoreMap":37},[41,454,455,458,461,464],{"class":43,"line":44},[41,456,457],{"class":47},"ssh",[41,459,460],{"class":51}," -J",[41,462,463],{"class":51}," deploy@bastion.example.com",[41,465,466],{"class":51}," deploy@10.0.1.15\n",[15,468,469],{},"To make it permanent in the config:",[32,471,473],{"className":34,"code":472,"language":36,"meta":37,"style":37},"Host bastion\n    HostName bastion.example.com\n    User deploy\n\nHost db-server\n    HostName 10.0.1.15\n    User deploy\n    ProxyJump bastion\n",[19,474,475,482,489,495,499,506,513,519],{"__ignoreMap":37},[41,476,477,479],{"class":43,"line":44},[41,478,262],{"class":47},[41,480,481],{"class":51}," bastion\n",[41,483,484,486],{"class":43,"line":71},[41,485,270],{"class":47},[41,487,488],{"class":51}," bastion.example.com\n",[41,490,491,493],{"class":43,"line":78},[41,492,279],{"class":47},[41,494,282],{"class":51},[41,496,497],{"class":43,"line":226},[41,498,315],{"emptyLinePlaceholder":314},[41,500,501,503],{"class":43,"line":285},[41,502,262],{"class":47},[41,504,505],{"class":51}," db-server\n",[41,507,508,510],{"class":43,"line":294},[41,509,270],{"class":47},[41,511,512],{"class":273}," 10.0.1.15\n",[41,514,515,517],{"class":43,"line":302},[41,516,279],{"class":47},[41,518,282],{"class":51},[41,520,521,524],{"class":43,"line":311},[41,522,523],{"class":47},"    ProxyJump",[41,525,481],{"class":51},[15,527,528,531],{},[19,529,530],{},"ssh db-server"," now hops through both servers in one step. The private key is never copied to the bastion.",[10,533,535],{"id":534},"_6-port-forwarding-tunneling","6. Port forwarding (tunneling)",[537,538,540],"h3",{"id":539},"local-forwarding-l","Local forwarding (-L)",[15,542,543,544,547],{},"Reach a service on the remote server that isn't exposed, from your own machine. For example PostgreSQL listening only on the server's ",[19,545,546],{},"localhost:5432",":",[32,549,551],{"className":34,"code":550,"language":36,"meta":37,"style":37},"ssh -N -L 5433:localhost:5432 prod\n# Locally: psql -h localhost -p 5433 -U app\n",[19,552,553,568],{"__ignoreMap":37},[41,554,555,557,560,563,566],{"class":43,"line":44},[41,556,457],{"class":47},[41,558,559],{"class":51}," -N",[41,561,562],{"class":51}," -L",[41,564,565],{"class":51}," 5433:localhost:5432",[41,567,265],{"class":51},[41,569,570],{"class":43,"line":71},[41,571,572],{"class":74},"# Locally: psql -h localhost -p 5433 -U app\n",[15,574,575,578,579,582],{},[19,576,577],{},"-N"," runs no remote command and just keeps the tunnel open. Add ",[19,580,581],{},"-f"," to send it to the background.",[15,584,585],{},"Through a bastion to another server on the private network:",[32,587,589],{"className":34,"code":588,"language":36,"meta":37,"style":37},"ssh -N -L 6380:10.0.1.20:6379 bastion    # local 6380 → 10.0.1.20:6379 (Redis)\n",[19,590,591],{"__ignoreMap":37},[41,592,593,595,597,599,602,605],{"class":43,"line":44},[41,594,457],{"class":47},[41,596,559],{"class":51},[41,598,562],{"class":51},[41,600,601],{"class":51}," 6380:10.0.1.20:6379",[41,603,604],{"class":51}," bastion",[41,606,607],{"class":74},"    # local 6380 → 10.0.1.20:6379 (Redis)\n",[537,609,611],{"id":610},"remote-forwarding-r","Remote forwarding (-R)",[15,613,614],{},"The reverse: makes a service on your machine reachable through the remote server.",[32,616,618],{"className":34,"code":617,"language":36,"meta":37,"style":37},"ssh -N -R 8080:localhost:3000 prod\n# On the server: curl localhost:8080 → :3000 on your machine\n",[19,619,620,634],{"__ignoreMap":37},[41,621,622,624,626,629,632],{"class":43,"line":44},[41,623,457],{"class":47},[41,625,559],{"class":51},[41,627,628],{"class":51}," -R",[41,630,631],{"class":51}," 8080:localhost:3000",[41,633,265],{"class":51},[41,635,636],{"class":43,"line":71},[41,637,638],{"class":74},"# On the server: curl localhost:8080 → :3000 on your machine\n",[537,640,642],{"id":641},"dynamic-forwarding-d","Dynamic forwarding (-D)",[15,644,645],{},"Opens a local SOCKS proxy, e.g. to route browser traffic through the server:",[32,647,649],{"className":34,"code":648,"language":36,"meta":37,"style":37},"ssh -N -D 1080 prod\ncurl --socks5-hostname localhost:1080 https:\u002F\u002Fifconfig.me\n",[19,650,651,664],{"__ignoreMap":37},[41,652,653,655,657,659,662],{"class":43,"line":44},[41,654,457],{"class":47},[41,656,559],{"class":51},[41,658,231],{"class":51},[41,660,661],{"class":273}," 1080",[41,663,265],{"class":51},[41,665,666,669,672,675],{"class":43,"line":71},[41,667,668],{"class":47},"curl",[41,670,671],{"class":51}," --socks5-hostname",[41,673,674],{"class":51}," localhost:1080",[41,676,677],{"class":51}," https:\u002F\u002Fifconfig.me\n",[679,680,681,697],"table",{},[682,683,684],"thead",{},[685,686,687,691,694],"tr",{},[688,689,690],"th",{},"Flag",[688,692,693],{},"Direction",[688,695,696],{},"Typical use",[698,699,700,714,727],"tbody",{},[685,701,702,708,711],{},[703,704,705],"td",{},[19,706,707],{},"-L local:target:port",[703,709,710],{},"Local → remote",[703,712,713],{},"Reach a remote DB\u002Fadmin panel",[685,715,716,721,724],{},[703,717,718],{},[19,719,720],{},"-R remote:target:port",[703,722,723],{},"Remote → local",[703,725,726],{},"Temporarily expose a local service",[685,728,729,734,737],{},[703,730,731],{},[19,732,733],{},"-D port",[703,735,736],{},"SOCKS proxy",[703,738,739],{},"Send all traffic out through the server",[10,741,743],{"id":742},"_7-copying-files","7. Copying files",[32,745,747],{"className":34,"code":746,"language":36,"meta":37,"style":37},"scp app.jar prod:\u002Fopt\u002Fapp\u002F\nscp -r prod:\u002Fvar\u002Flog\u002Fmyapp .\u002Flogs\nrsync -avz --progress dist\u002F prod:\u002Fvar\u002Fwww\u002Fapp\u002F\nrsync -avz --delete -e \"ssh -p 2222\" dist\u002F deploy@203.0.113.10:\u002Fvar\u002Fwww\u002Fapp\u002F\nsftp prod\n",[19,748,749,760,773,790,815],{"__ignoreMap":37},[41,750,751,754,757],{"class":43,"line":44},[41,752,753],{"class":47},"scp",[41,755,756],{"class":51}," app.jar",[41,758,759],{"class":51}," prod:\u002Fopt\u002Fapp\u002F\n",[41,761,762,764,767,770],{"class":43,"line":71},[41,763,753],{"class":47},[41,765,766],{"class":51}," -r",[41,768,769],{"class":51}," prod:\u002Fvar\u002Flog\u002Fmyapp",[41,771,772],{"class":51}," .\u002Flogs\n",[41,774,775,778,781,784,787],{"class":43,"line":78},[41,776,777],{"class":47},"rsync",[41,779,780],{"class":51}," -avz",[41,782,783],{"class":51}," --progress",[41,785,786],{"class":51}," dist\u002F",[41,788,789],{"class":51}," prod:\u002Fvar\u002Fwww\u002Fapp\u002F\n",[41,791,792,794,796,799,802,804,807,810,812],{"class":43,"line":226},[41,793,777],{"class":47},[41,795,780],{"class":51},[41,797,798],{"class":51}," --delete",[41,800,801],{"class":51}," -e",[41,803,62],{"class":61},[41,805,806],{"class":51},"ssh -p 2222",[41,808,809],{"class":61},"\"",[41,811,786],{"class":51},[41,813,814],{"class":51}," deploy@203.0.113.10:\u002Fvar\u002Fwww\u002Fapp\u002F\n",[41,816,817,820],{"class":43,"line":285},[41,818,819],{"class":47},"sftp",[41,821,265],{"class":51},[15,823,824,825,827],{},"Prefer ",[19,826,777],{}," for large or repeated transfers: it sends only changed files and can resume an interrupted transfer.",[10,829,831],{"id":830},"_8-server-side-hardening","8. Server-side hardening",[15,833,834,837,838,547],{},[433,835,836],{},"After"," confirming key login works, set in ",[19,839,840],{},"\u002Fetc\u002Fssh\u002Fsshd_config",[32,842,844],{"className":34,"code":843,"language":36,"meta":37,"style":37},"PasswordAuthentication no\nPermitRootLogin no\nPubkeyAuthentication yes\nMaxAuthTries 3\n",[19,845,846,854,861,868],{"__ignoreMap":37},[41,847,848,851],{"class":43,"line":44},[41,849,850],{"class":47},"PasswordAuthentication",[41,852,853],{"class":51}," no\n",[41,855,856,859],{"class":43,"line":71},[41,857,858],{"class":47},"PermitRootLogin",[41,860,853],{"class":51},[41,862,863,866],{"class":43,"line":78},[41,864,865],{"class":47},"PubkeyAuthentication",[41,867,308],{"class":51},[41,869,870,873],{"class":43,"line":226},[41,871,872],{"class":47},"MaxAuthTries",[41,874,390],{"class":273},[32,876,878],{"className":34,"code":877,"language":36,"meta":37,"style":37},"sudo sshd -t                       # test the configuration\nsudo systemctl reload sshd         # the service is called ssh on Ubuntu\n",[19,879,880,893],{"__ignoreMap":37},[41,881,882,885,888,890],{"class":43,"line":44},[41,883,884],{"class":47},"sudo",[41,886,887],{"class":51}," sshd",[41,889,52],{"class":51},[41,891,892],{"class":74},"                       # test the configuration\n",[41,894,895,897,900,903,905],{"class":43,"line":71},[41,896,884],{"class":47},[41,898,899],{"class":51}," systemctl",[41,901,902],{"class":51}," reload",[41,904,887],{"class":51},[41,906,907],{"class":74},"         # the service is called ssh on Ubuntu\n",[909,910,911],"blockquote",{},[15,912,913],{},"Don't close your current SSH session while applying this. Keep it open until you've confirmed you can connect from a new terminal; otherwise you may lock yourself out.",[15,915,916,917,920],{},"Installing ",[19,918,919],{},"fail2ban"," is a good extra measure against brute-force attempts.",[10,922,924],{"id":923},"_9-troubleshooting","9. Troubleshooting",[679,926,927,937],{},[682,928,929],{},[685,930,931,934],{},[688,932,933],{},"Error",[688,935,936],{},"Cause \u002F Fix",[698,938,939,956,971,983,1000,1014,1024],{},[685,940,941,946],{},[703,942,943],{},[19,944,945],{},"Permission denied (publickey)",[703,947,948,949,952,953],{},"The public key isn't in ",[19,950,951],{},"authorized_keys"," or wrong user; see which key is tried with ",[19,954,955],{},"ssh -v",[685,957,958,961],{},[703,959,960],{},"Key is set up but it still asks for a password",[703,962,963,964,967,968,970],{},"Wrong permissions: ",[19,965,966],{},"~\u002F.ssh"," 700, ",[19,969,951],{}," 600, home dir not writable by others",[685,972,973,978],{},[703,974,975],{},[19,976,977],{},"UNPROTECTED PRIVATE KEY FILE",[703,979,980],{},[19,981,982],{},"chmod 600 ~\u002F.ssh\u002Fid_ed25519",[685,984,985,990],{},[703,986,987],{},[19,988,989],{},"Too many authentication failures",[703,991,992,993,995,996,999],{},"Add ",[19,994,421],{}," and the right ",[19,997,998],{},"IdentityFile"," to the config",[685,1001,1002,1007],{},[703,1003,1004],{},[19,1005,1006],{},"REMOTE HOST IDENTIFICATION HAS CHANGED",[703,1008,1009,1010,1013],{},"If the server was reinstalled, ",[19,1011,1012],{},"ssh-keygen -R 203.0.113.10","; otherwise be careful, it could be MITM",[685,1015,1016,1019],{},[703,1017,1018],{},"Idle connections drop",[703,1020,1021],{},[19,1022,1023],{},"ServerAliveInterval 60",[685,1025,1026,1032],{},[703,1027,1028,1031],{},[19,1029,1030],{},"bind: Address already in use"," (tunnel)",[703,1033,1034],{},"The local port is taken; pick another one",[15,1036,1037,1038,1041,1042,1045],{},"For any connection problem, the first step is verbose output: ",[19,1039,1040],{},"ssh -v prod"," (",[19,1043,1044],{},"-vvv"," for more).",[10,1047,1049],{"id":1048},"conclusion","Conclusion",[15,1051,1052,1053,1056,1057,1060],{},"An ed25519 key, a well-organized ",[19,1054,1055],{},"~\u002F.ssh\u002Fconfig"," and tunnels opened with ",[19,1058,1059],{},"-L"," turn SSH from \"the tool for logging into servers\" into the center of daily work. On the server side, disabling password and root logins are the two most effective security measures you can take.",[1062,1063,1064],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":37,"searchDepth":71,"depth":71,"links":1066},[1067,1068,1069,1070,1071,1072,1073,1078,1079,1080,1081],{"id":12,"depth":71,"text":13},{"id":29,"depth":71,"text":30},{"id":115,"depth":71,"text":116},{"id":179,"depth":71,"text":180},{"id":243,"depth":71,"text":244},{"id":443,"depth":71,"text":444},{"id":534,"depth":71,"text":535,"children":1074},[1075,1076,1077],{"id":539,"depth":78,"text":540},{"id":610,"depth":78,"text":611},{"id":641,"depth":78,"text":642},{"id":742,"depth":71,"text":743},{"id":830,"depth":71,"text":831},{"id":923,"depth":71,"text":924},{"id":1048,"depth":71,"text":1049},"2026-10-31","Generating ed25519 keys, ssh-agent, shortcuts with ~\u002F.ssh\u002Fconfig, jump hosts, port forwarding (-L, -R, -D) and server-side hardening.","md",{},"\u002Fen\u002Fblog\u002Fssh-config-and-tunneling",{"title":5,"description":1083},"en\u002Fblog\u002Fssh-config-and-tunneling",[457,1090,1091,1092],"linux","security","guide","\u002Fblog\u002Fssh-config-and-tunneling","VU7_KW2y7-uQE1pmTUfVsMlXW5ao09lY5oHAFxSBSE0",[1096,1104,1111],{"path":1097,"title":1098,"description":1099,"date":1100,"tags":1101},"\u002Fen\u002Fblog\u002Fsystemd-service-guide","Writing a Linux Service with systemd","Running an app as a systemd service — unit files, restart policies, environment variables, reading logs with journalctl, and scheduled jobs with timers.","2026-10-30",[1090,1102,1103,1092],"systemd","devops",{"path":1105,"title":1106,"description":1107,"date":1108,"tags":1109},"\u002Fen\u002Fblog\u002Fnginx-reverse-proxy-ssl","Setting Up an Nginx Reverse Proxy with Let's Encrypt SSL","Putting a Node.js or Spring Boot app behind Nginx, getting a free SSL certificate with Certbot, and setting up automatic renewal.","2026-10-28",[1110,1090,1103,1092],"nginx",{"path":1112,"title":1113,"description":1114,"date":1115,"tags":1116},"\u002Fen\u002Fblog\u002Flinux-best-practices","Linux Best Practices","Frequently used Linux system commands and good practices: CPU, memory, disk, user, and port management.","2022-10-31",[1090],1793407401297]