6 điểm bởi GN⁺ 2023-08-24 | 1 bình luận | Chia sẻ qua WhatsApp
  • SSH không chỉ là công cụ truy cập từ xa mà còn bao quát cả chuyển tiếp cổng, jump host, tệp cấu hình và quản lý khóa; các ví dụ với máy chủ web cũng có thể áp dụng nguyên vẹn cho các dịch vụ khác như RDP·SQL
  • Chuyển tiếp cổng cục bộ·từ xa·động lần lượt được cấu hình bằng -L, -R, -D, và khác biệt cốt lõi là cổng được mở ở phía nào và lưu lượng đi theo hướng nào
  • Trong các mạng không cho phép truy cập trực tiếp, có thể dùng jump host -JProxyJump để nối nhiều điểm trung chuyển SSH rồi đi vào đích cuối
  • ssh-agentagent forwarding -A giúp dùng khóa cục bộ ngay cả trên máy chủ từ xa, rất tiện, nhưng cần kiểm tra trước các rủi ro bảo mật do việc lạm dụng agent
  • Khi dùng cùng ~/.ssh/config, ssh-copy-id, ssh-keygen, và console SSH ~?·~C, bạn có thể giảm việc nhập lặp lại các tùy chọn, thêm forwarding trong phiên, phân phối khóa công khai, cũng như tạo·kiểm tra khóa dễ hơn

Những điều cần biết trước để hiểu SSH port forwarding

  • Chỉ nhìn sơ đồ thì khá khó nắm được luồng của SSH port forwarding, nên sẽ dễ hiểu hơn nếu xem cùng các lệnh thực tế và kịch bản mạng
  • Ví dụ dựa trên việc truy cập máy chủ web, nhưng cách làm tương tự có thể áp dụng cho RDP, SQL và gần như mọi dịch vụ khác
  • Các tùy chọn dùng lặp lại có ý nghĩa như sau
    • -N: không thực thi lệnh trên máy chủ từ xa và cũng không mở shell
    • -f: đưa SSH xuống chạy nền
    • root@host: đăng nhập bằng người dùng và host sẽ dùng để tạo tunnel

Chuyển tiếp cổng cục bộ -L

  • Chuyển tiếp cổng cục bộ chuyển một cổng trên máy cục bộ tới một cổng trên máy chủ từ xa
  • Tình huống ví dụ
    • internal-web.int đang chạy một trang web chỉ truy cập được qua giao diện loopback
    • Có thể SSH từ campfire.int tới internal-web.int
    • Muốn truy cập máy chủ web của internal-web.int thông qua một cổng cục bộ trên campfire.int
  • Lệnh sử dụng
    • ssh -N -f -L 1337:127.0.0.1:80 root@internal-web.int
  • Giải thích tùy chọn
    • -L: chỉ định chuyển tiếp cổng cục bộ
    • 1337:127.0.0.1:80: bind cổng cục bộ 1337 tới 127.0.0.1:80 ở phía từ xa
  • Sau khi tạo tunnel, có thể gửi yêu cầu tới cổng cục bộ 1337 trên campfire.int để tương tác với cổng 80 của internal-web.int
  • Điều cần nhớ là trong -L, cổng cục bộ nằm ở bên trái của địa chỉ

Chuyển tiếp cổng từ xa -R

  • Chuyển tiếp cổng từ xa làm lộ ra trên máy chủ từ xa một cổng mà phía cục bộ có thể truy cập được
  • Tình huống ví dụ
    • internal-web.int đang chạy một trang web chỉ truy cập được qua loopback
    • campfire.int không thể truy cập trực tiếp internal-web.int do tường lửa
    • vuln-server.int có thể được truy cập từ cả campfire.int lẫn internal-web.int
  • Lệnh sử dụng
    • ssh -N -f -R 3000:127.0.0.1:80 root@vuln-server.int
  • Giải thích tùy chọn
    • -R: chỉ định chuyển tiếp cổng từ xa
    • 3000:127.0.0.1:80: bind cổng 3000 trên vuln-server.int tới 127.0.0.1:80 ở phía cục bộ
  • Sau đó, nếu gửi yêu cầu curl tới vuln-server.int:3000, có thể truy cập trang web nội bộ đang chạy trên cổng 80 của internal-web.int
  • Điều cần nhớ là trong -R, cổng cục bộ nằm ở bên phải của địa chỉ

Chuyển tiếp cổng động -D và proxy SOCKS

  • Chuyển tiếp cổng động dùng tùy chọn -D để tạo một proxy SOCKS và gửi lưu lượng qua điểm trung chuyển SSH
  • Tình huống ví dụ
    • internal-web.int đang chạy một ứng dụng web chỉ truy cập được từ mạng nội bộ
    • vuln-server.int nằm trong cùng mạng nội bộ và có thể truy cập internal-web.int
    • Muốn proxy lưu lượng từ campfire.int thông qua vuln-server.int
  • Cấu hình /etc/proxychains.conf phải khớp với cổng trong lệnh SSH
    • socks5: để proxychains dùng SOCKS5
    • 127.0.0.1: dùng localhost
    • 8080: phải trùng với cổng đã chỉ định cho SSH -D
  • Lệnh sử dụng
    • ssh -N -f -D 8080 root@vuln-server.int
  • Sau khi tạo forwarding, nếu cấu hình socks5 127.0.0.1 8080 thì có thể truy cập trang web nội bộ bằng proxychains curl 192.168.1.185
  • DNS qua SOCKS đôi khi hoạt động không ổn tùy môi trường, nên trong ví dụ dùng địa chỉ IP thay vì hostname
  • Trong Firefox cũng có thể dùng proxy SOCKS bằng cấu hình proxy thủ công
    • Đường dẫn: Settings → Privacy & Security → Network Settings
    • Chọn Manual proxy configuration
    • Tích “Proxy DNS when using SOCKS V5”
    • Đặt SOCKS host là 127.0.0.1, port là 8080

Jump host -J

  • Jump host cho phép kết nối tới một đích không thể truy cập trực tiếp từ máy hiện tại bằng cách đi qua nhiều điểm trung chuyển SSH
  • Chuỗi ví dụ là campfire.intvuln-server.intinternal-web.intdns.int
  • Lệnh sử dụng
    • ssh -J root@vuln-server.int,root@internal-web.int root@dns.int
  • Nhiều điểm nhảy được phân tách bằng dấu phẩy

Agent forwarding -A

  • ssh-agent cho phép thêm private key hoặc identity trên máy cục bộ bằng ssh-add <private_key_file>
  • Có thể kiểm tra các khóa đã thêm bằng ssh-add -l
  • Khi thêm khóa vào ssh-agent, bạn có thể SSH bằng khóa đó mà không cần nhập lại mật khẩu, hữu ích cho cả con người lẫn tài khoản dịch vụ
  • Agent forwarding -A cho phép dùng các khóa trong agent cục bộ ngay cả trên máy từ xa mà bạn đã kết nối tới
  • Trước khi dùng, hãy xem rủi ro bảo mật tại Zero Effort Private Key Compromise: Abusing SSH-Agent for Lateral Movement
  • Lệnh ví dụ
    • ssh -A -J root@vuln-server.int root@internal-web.int
  • Lệnh này kết nối tới internal-web.int qua vuln-server.int, đồng thời cho phép dùng các khóa trong SSH agent của campfire.int
  • Sau đó, từ internal-web.int, khi chạy ssh root@dns.int, bạn có thể đăng nhập mà không cần chỉ định private key hay nhập thông tin xác thực

Gán lệnh TTY -t

  • Tùy chọn -t hữu ích khi cần nhanh chóng chạy một lệnh tương tác trên máy chủ từ xa
  • Ví dụ là các lệnh cần TTY như Vim hoặc top
  • Lệnh sử dụng
    • ssh root@internal-web.int -t top
  • Khi thực thi, bạn sẽ nhận được một TTY trên máy chủ từ xa với lệnh top

-g để host bên ngoài cũng có thể kết nối vào cổng local forwarding

  • Tùy chọn -g cho phép các host từ xa kết nối tới cổng được local forwarding
  • Nó tương tự local port forwarding -L, nhưng khác ở chỗ các máy bên ngoài cũng có thể dùng cổng “cục bộ” đó
  • Tình huống ví dụ
    • Có quyền truy cập shell vào vuln-server.int
    • Muốn proxy các kết nối đến cổng 2222 của vuln-server.int sang cổng 22 của internal-web.int
  • Lệnh sử dụng
    • ssh -N -f -g -L 2222:localhost:22 root@internal-web.int
  • Giải thích tùy chọn
    • -g: cho phép host từ xa kết nối tới cổng local forwarding
    • -L: chỉ định local forwarding
  • Dù SSH vào cổng 2222 của vuln-server.int, shell thực tế vẫn sẽ nằm trên internal-web.int

Console SSH ~? và forwarding trong phiên

  • Console SSH là một tính năng ẩn cho phép điều khiển chính SSH mà không tương tác trực tiếp với hệ thống từ xa
  • Nó hữu ích khi shell bị lỗi hoặc khi cần điều khiển chính phiên SSH
  • Có thể mở console trợ giúp bằng ~?
  • Một số tùy chọn hữu ích
    • ~.: kết thúc phiên SSH hiện tại
    • ~C: mở console SSH để thêm các tùy chọn forwarding
  • Ngay cả khi đã kết nối tới vuln-server.int bằng lệnh ssh thông thường, bạn vẫn có thể nhấn ~C rồi nhập -D 8080 để biến phiên đó thành một phiên dynamic forwarding
  • Nếu /etc/proxychains.conf trên campfire.int đã được cấu hình dùng cổng 8080, thì có thể dùng proxychains như thể bạn đã bắt đầu bằng ssh -D ngay từ đầu

Tệp cấu hình SSH ~/.ssh/config

  • Tệp cấu hình SSH nằm tại ~/.ssh/config, giúp lưu các tùy chọn SSH thường nhập lặp lại để tiết kiệm thời gian
  • Khi SSH kết nối, nó sẽ phân tích tệp này và nếu có cấu hình host khớp với đích kết nối thì sẽ dùng các tùy chọn tương ứng
  • Đối số trên dòng lệnh có độ ưu tiên cao hơn tệp cấu hình
    • Ví dụ, dù trong tệp cấu hình có đặt người dùng cho internal-web.introot, nếu chạy ssh graham@internal-web.int thì SSH sẽ thử đăng nhập bằng graham
  • Ví dụ cấu hình cơ bản
# You can put comments with a `#` at the beginning of the line only.
host internal-web.int
    User root
    IdentityFile /home/smores/ssh_agent/internal-web-no-pw
    Port 2222
  • Luồng xử lý khi chạy ssh internal-web.int
    • So khớp internal-web.int trên dòng lệnh với host internal-web.int trong ~/.ssh/config
    • Nếu khớp, lấy các tùy chọn chưa được chỉ định trên dòng lệnh từ tệp cấu hình
    • Nếu không khớp, chỉ dùng các tùy chọn đã định nghĩa trên dòng lệnh

Các từ khóa SSH config thường dùng

  • IdentityFile /path/to/private_key
    • Chỉ định private key sẽ dùng cho host
    • Có vai trò tương tự ssh -i
  • ForwardAgent
  • ProxyJump root@internal-web.int
    • Chỉ định máy chủ sẽ dùng để proxy lưu lượng
    • Có vai trò tương tự tùy chọn -J
    • Trong ví dụ, nó yêu cầu xác thực với vuln-server.int trước, cho thấy lưu lượng đi qua host đó
  • Match
    • Áp dụng các từ khóa SSH config theo điều kiện
    • Trong ví dụ, nếu mã thoát của lệnh export | grep PROXYME=TRUE0 thì dùng ProxyJump dưới khối Match
    • Nếu không có biến môi trường PROXYME thì chỉ dùng khối host internal-web.int thông thường
    • Sau khi đặt export PROXYME=TRUE, nếu chạy cùng lệnh ssh internal-web.int thì trước tiên sẽ xác thực qua vuln-server.int rồi mới nhận shell của internal-web.int
  • scp và một số tiện ích dựa trên SSH khác cũng thường có thể dùng SSH config
    • Trong môi trường không tự động dùng, có thể chỉ rõ bằng -F ~/.ssh/config

Sao chép public key ssh-copy-id

  • ssh-copy-id là một tiện ích nhỏ để nhanh chóng tải public key lên máy chủ
  • Lệnh sử dụng
    • ssh-copy-id -i internal-web root@internal-web.int
  • Giải thích tùy chọn
    • -i internal-web: chỉ định tên private key sẽ dùng để xác thực với máy chủ
    • root@internal-web.int: chỉ định máy chủ sẽ tải public key lên

Tạo và kiểm tra khóa bằng ssh-keygen

  • ssh-keygen là tiện ích để tạo cặp private key·public key
  • Thông thường khuyến nghị dùng tùy chọn -b để chỉ định kích thước khóa lớn hơn
  • Kích thước khóa mặc định trong môi trường ví dụ là 3072
  • Thuật toán mặc định là RSA, nhưng có thể chỉ định thuật toán khác bằng cờ -t
    • Ví dụ: ssh-keygen -t ecdsa -b 521
  • Có thể kiểm tra fingerprint và kích thước byte của khóa bằng lệnh sau
    • ssh-keygen -lf <file-name>

1 bình luận

 
GN⁺ 2023-08-24
Ý kiến trên Hacker News
  • Ở đây thiếu một chỉ thị đơn giản đến đáng ngạc nhiên: chỉ cần cấu hình trong sshd_config kiểu AuthorizedKeysCommand /usr/bin/php /etc/ssh/auth.php %u, rồi trong script lấy https://github.com/{$user}.keys từ GitHub là được
    Tất nhiên đây không phải mã đạt chất lượng production, nhưng nó cho thấy ý chính
    Sau khi kiểm tra người dùng có thuộc tổ chức/nhóm GitHub hay không, nếu người dùng tồn tại và được ánh xạ bằng thứ như nss-ato, bạn có thể cho phép đăng nhập vào máy chủ
    Khi onboarding/offboarding nhân sự, chỉ cần thêm/xóa họ khỏi nhóm GitHub là có thể cấp hoặc thu hồi quyền truy cập thiết bị, giảm bớt phiền phức

    • Amazon Linux cũng làm việc tương tự, nhưng có lẽ vì lý do chất lượng production nên phức tạp hơn nhiều
      Trên Amazon Linux 2 trở xuống, nó gọi dòng lệnh openssl để kiểm tra định dạng từng khóa trong file authorized_keys, nhưng lại hard-code RSA, nên dù phiên bản OpenSSH có hỗ trợ ed25519, bạn vẫn không thể xác thực bằng ed25519 trên host Amazon Linux 2
      Về lý thuyết, điều này tốt vì cho phép một tính năng¹ khá hay, nhưng trên thực tế nó làm hỏng cả chức năng cơ bản đối với những người không dùng tính năng đó, khiến tôi kém tin tưởng Amazon Linux hơn
      Lần đầu gặp vấn đề này là khi tôi đang cố SSH vào một máy do một đồng nghiệp DevOps theo hướng cloud-first sở hữu, và vì không thể trực tiếp đụng vào máy nên rất khó chẩn đoán
      Anh ấy rành AWS nhưng kém rành Linux hơn nên không biết phải xem ở đâu; anh chọn Amazon Linux vì nghĩ rằng đây là bản phân phối do chủ sở hữu nền tảng cloud tạo ra nên sẽ “tương thích hơn”, nhưng “tương thích hơn” ở đây thực ra nghĩa là “có nhiều bất ngờ ngớ ngẩn hơn”
      ¹ https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-...
    • Nhìn mấy thứ như vậy lại thấy tiếc vì mình ở phía mạng
      Vì “mạng không chạy đúng” là phạm vi công việc, nên tôi bỏ lỡ những tính năng hay ho như thế này
    • Với mục đích kiểu này thì nên dùng chứng chỉ SSH thay vào đó
  • Có lẽ nhiều người không biết rằng parser cấu hình của OpenSSH bỏ qua các chỉ thị trùng lặp, và chỉ chỉ thị đầu tiên trong số các chỉ thị giống nhau mới có hiệu lực
    Thông thường trong parser cấu hình hoặc rule engine, chỉ thị xuất hiện sau sẽ ghi đè chỉ thị trước, nên điều này khá trái trực giác
    Nghe có vẻ nhỏ nhặt, nhưng khi thay đổi các giá trị mặc định như /etc/ssh/sshd_config, cả con người lẫn phần mềm thường có xu hướng thêm thay đổi vào cuối file hoặc cuối khối chỉ thị, và kỳ vọng nó sẽ được áp dụng
    Các công ty bảo mật, tổ chức, và nhiều sản phẩm bastion SSH cũng hiểu sai điểm này; các khuyến nghị CIS Benchmarks và hầu hết công cụ audit CIS của bên thứ ba cũng không xét đến thứ tự ưu tiên hoặc xử lý sai
    Để xác nhận các chỉ thị cấu hình OpenSSH có được định nghĩa đúng như kỳ vọng hay không, đừng đọc lướt file cấu hình trực tiếp; hãy dump cấu hình nội bộ đã được suy diễn bằng sshd -T hoặc ssh -G

    • File sudoers cũng hoạt động theo cách tương tự
      Với phần mềm xác thực/ủy quyền người dùng, tôi cho rằng cách này là đáng mong muốn
      Vì nó giúp dễ tạo ra các thiết lập không thể bị ghi đè chỉ bằng cách thêm file mới vào thư mục whatever.conf.d
      Chỉ cần định nghĩa thiết lập đó trong file cấu hình chính trước khi nạp whatever.conf.d/*, rồi đặt bảo vệ đặc biệt cho file đó
      Ngay cả khi không có ai cố vượt qua kiểm soát, điều này vẫn có lợi cho quản lý cấu hình, vì nếu một người mới chưa nắm toàn bộ bối cảnh thêm file mới, hoặc một gói nào đó cài các mặc định kỳ quặc cho dịch vụ của nó, thì cấu hình chuẩn vẫn giữ được ưu tiên
      Lý do trong các bối cảnh khác ta thường thấy hành vi ngược lại là vì thứ mong muốn không phải “cấu hình chuẩn”, mà là giá trị mặc định theo nghĩa chặt chẽ, dùng khi người dùng/nhà phát triển/quản trị viên không thiết lập rõ ràng
    • Có vẻ một số chỉ thị có thể trùng lặp. Ví dụ như AllowUsers
      Tuy vậy, hôm qua NixOS đột nhiên đổi thứ tự merge, khiến AllowUsers trong file của tôi bị đẩy xuống dưới Match của file khác và bị kẹt ở đó
  • Câu trong bài nói rằng với -L thì phía local nằm bên trái của địa chỉ trong local forwarding, còn với -R thì port local nằm bên phải của địa chỉ trong remote forwarding là câu quan trọng và súc tích nhất
    Ngay từ đầu tôi đã nhầm lẫn -L-R, và việc instance port nào là local lại thay đổi theo L/R khá phiền
    Tôi hiểu rằng -L-R thay đổi hướng chủ ý, tức bên khởi tạo và bên phản hồi nằm ở đâu, nhưng có lẽ cũng ổn nếu port:address:port luôn mang nghĩa local:binding:remote, còn -L/-R quyết định phía nào là bên lắng nghe và phía nào là bên gửi

    • Cách dễ nhớ nhất là -L lắng nghe trên port local là con số xuất hiện ngay sau nó, còn -R lắng nghe trên port remote là con số xuất hiện ngay sau nó
      Phần host:port còn lại chỉ là định dạng thông thường cho biết sẽ kết nối tới đâu
      Vì đây là port forwarding qua đường hầm SSH, nên cũng tự nhiên suy ra rằng host sẽ được liên hệ từ phía đối diện đường hầm so với nơi có port đang lắng nghe
  • Trong SSH có một tính năng ít được biết đến nhưng hữu ích là ghép kênh kết nối
    Thay vì tạo một kết nối TCP mới và thực hiện lại quy trình xác thực, có thể cấu hình để tái sử dụng kết nối hiện có
    Bản thân giao thức có khái niệm kênh, và mỗi khung dữ liệu được gắn metadata để phân biệt các luồng khác nhau; tính năng này tận dụng cơ chế đó
    Lợi ích lớn là các phiên sau không cần xác thực đầy đủ lại từ đầu
    Đặc biệt hữu ích khi máy từ xa không có tmux hay công cụ tương tự và bạn dùng nhiều cửa sổ terminal như nhiều panel; nếu xác thực có bước mất vài giây như passphrase hoặc chạm HSM thì càng cảm nhận rõ hơn
    Cũng có thiết lập “duy trì kết nối”, nên khi qua lại giữa vài máy chủ sẽ không phải xác thực mỗi lần
    Nhìn chung đây là tính năng có thì tốt, nhưng không đến mức thay đổi cuộc đời
    Một số máy chủ tắt tính năng này; khi bị tắt thì sự thiếu vắng của nó lại dễ nhận ra hơn so với khi được bật
    Xem thêm: https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing

    • Nếu độ trễ giữa client và server lớn thì khác biệt rất đáng kể
      SSH là một giao thức khá “nói nhiều”, không giống TLS vốn được tối ưu để giảm số vòng trao đổi, và tùy chọn ghép kênh này gần như là ngoại lệ duy nhất
    • Có thể khiến Ansible dễ dùng hơn nhiều khi đi qua bastion host bằng ProxyJump
  • Nếu có nhiều host trong ~/.ssh/config, có thể dùng chỉ thị Include hỗ trợ wildcard để tránh làm file quá rối
    Ví dụ, trong ~/.ssh/config đặt Include config.d/*.conf, rồi tách các thiết lập host, hostname, user vào ~/.ssh/config.d/work.conf hoặc client1.conf

    • Chỉ thị Host cũng hỗ trợ wildcard
      Ví dụ, có thể thêm host *_work và đặt các thiết lập chung cho mọi host dùng cho công việc như host1_work
    • Một mẹo bổ sung là có thể đặt Include bên trong chỉ thị Host/Match
      Ví dụ, nếu trong ~/.ssh/configHost proj1.*.corpInclude ~/.ssh/proj1.conf, bạn có thể đặt các match theo dự án ở gần phía trên, đồng thời giảm việc phải lục qua rất nhiều file riêng lẻ khi rà soát
  • Khi forwarding, hầu như không dùng -f
    Nó có thể trở thành khẩu súng dưới chân mình, vì khiến khó biết forwarding nào vẫn đang mở và đang hoạt động
    -t là một mẹo hay, và là tính năng trước đây tôi không biết
    Một điểm quan trọng dễ bị bỏ sót trong danh sách lệnh escape ~ là ngay cả trong phiên lồng nhau cũng có thể lồng escape
    Điều này hữu ích khi vì lý do nào đó không dùng -J
    Danh sách này khớp với những thứ hữu ích, và tôi cũng học thêm được vài điều

    • -t rất tuyệt. Tôi dùng kiểu ssh -t my-dev-vps 'tmux new-session -A -s main' để mỗi lần chạy là quay thẳng về vị trí trước đó trong phiên tmux
    • Nếu đang mở nhiều shell trên máy chủ đích thì vẫn có cùng vấn đề
      Sẽ tốt hơn nếu SSH cung cấp trạng thái forwarding theo một cách hợp lý, thay vì phải tự đào trong danh sách tiến trình
  • Hiện có một pull request đang thêm hỗ trợ AF_UNIX; nếu được đưa vào, nó sẽ mở ra đủ loại forwarding thú vị
    Vì khi đó sẽ dễ proxy kết nối SSH qua một tiến trình cục bộ tùy ý, và tiến trình đó có thể tự xử lý việc chuyển dữ liệu đến đầu xa theo ý muốn
    https://github.com/openssh/openssh-portable/pull/431

    • Thứ tôi quan tâm là -D dùng AF_UNIX, nhưng việc mọi thứ có thể hoạt động trên AF_UNIX cũng rất hay
      Có vẻ từ khoảng một năm trước, curl đã có thể dùng SOCKS qua AF_UNIX thông qua cú pháp ALL_PROXYsocks5://localhost/path hoặc socks5h
      Có lẽ tính năng này được thêm vì Tor dùng proxy SOCKS AF_UNIX
      Sẽ tốt nếu có thể cấu hình quyền truy cập mạng bằng quyền Unix tiêu chuẩn; cá nhân tôi còn muốn có thể đẩy TCP/IP hoàn toàn ra khỏi kernel thì càng tốt
  • Lần đầu nhìn thấy console của SSH tôi đã rất sốc
    Đồng nghiệp cho tôi xem ~#, và cảm giác như vừa phát hiện ra menu cheat bí mật kiểu có thể xuất hiện trong game SEGA Genesis

  • Vì sao lại là dấu ngã? Vì rlogin, rsh đã dùng nó
    Vì sao rlogin, rsh dùng dấu ngã? Vì cu đã dùng nó
    Vì sao là cu? Khi có modem hoặc đường serial, người ta giao tiếp bằng cu, và phải gửi mã Hayes; nhưng nếu dùng chuỗi escape của mã Hayes thì sẽ thoát vào modem, nên cần một tín hiệu riêng để thoát khỏi cu
    Vì sao không phải ^[? Vì đó là telnet
    Vì vậy nếu bạn telnet vào một host rồi dùng cu để kết nối tới modem, bạn cần một cú pháp escape riêng để quay lại cu mà không thoát về telnet
    Rốt cuộc đây là một cấu trúc các cú pháp escape chồng chất vô tận
    Và thực ra không phải là dấu ngã, mà là dấu ngã

    • Tôi cứ tưởng thứ tự là CR, dấu ngã, rồi .; chẳng lẽ bấy lâu nay tôi dùng sai sao?
  • Phần ssh-copy-id ban đầu giải thích rằng lệnh này tải public key lên, rồi đột nhiên chuyển sang nói tải private key lên; có vẻ là lỗi đánh máy
    Ngoài ra lệnh này không chỉ đơn giản là tải key lên, mà còn nối thêm vào ~/.ssh/authorized_keys, nên hữu ích hơn nhiều
    Cuối cùng, ở phần ssh-keygen, theo những tài liệu tôi đọc gần đây thì ed25519 được ưu tiên hơn ecdsa