New FAMILUG

The PyMiers

Sunday, 5 July 2026

Cài đặt và chạy máy ảo OpenBSD 6.8 trên VirtualBox 6.0

Nếu vì một lý do gì bạn không dùng vagrant (không tin tưởng người build vagrant box chẳng hạn), việc tạo máy ảo OpenBSD trên VirtualBox không có gì phức tạp, nhưng cần chút chú ý.

Tải file installXX.iso OpenBSD mới nhất

Truy cập https://www.openbsd.org/faq/faq4.html#Download

rồi tìm tới file installXX.iso, chọn architecture phù hợp, thường là amd64. Chú ý là file .iso để tạo đĩa CD/cài máy ảo. File .img là dùng để tạo USB.

Tạo máy ảo mới trên VirtualBox

New > BSD > OpenBSD 64 bits

Continue > Continue .... > Create

Trước khi bật máy ảo lên, chọn Settings > Storage > Chọn hình đĩa CD rồi chọn tới file ISO vừa tải.

virtualbox

QUAN TRỌNG: chọn Audio > bỏ tick "Enable audio" đi, nếu không, VirtualBox sẽ full CPU sau khi sleep mặc dù xem trên OpenBSD thì không thấy dùng CPU.

Settings > Network > Adapter 1 > NAT > Port forwarding, thêm một dòng mới, chọn host port là 1 số từ 2222 đến 22XX, Guest Port là 22.

Port forwarding

Cài OpenBSD

Bật máy ảo lên, Install > hầu hết là enter chọn các option mặc định, mất tầm 2 phút để cài. Khi có hỏi xedodm, chọn No, khi hỏi sshd, chọn yes.

Cài xong reboot máy ảo, vào Settings > Storage > bỏ file ISO đã nhét vào ra. Máy sẽ boot vào OpenBSD.

SSH vào OpenBSD

Chuột phải chọn máy ảo > Start > Headless start. Chờ vài giây rồi ssh vào máy:

ssh root@127.0.0.1 -p 2222

Nhập password đã chọn lúc cài đặt.

$ ssh root@127.0.0.1 -p2222
root@127.0.0.1's password: 
Last login: Wed Feb 10 22:35:32 2021 from 10.0.2.2
OpenBSD 6.8 (GENERIC) #97: Sun Oct  4 18:00:46 MDT 2020

Welcome to OpenBSD: The proactively secure Unix-like operating system.

Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code.  With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.

obsd#                                      

Xong.

Kết luận

Cài đặt máy ảo OpenBSD là một việc đơn giản, nhanh chóng. Have fun.

Kết luận

Hết.

HVN at https://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺

Tính hash cho hostname trong SSH known_hosts

SSH known host là gì

SSH client lưu trữ danh sách các host key của tất cả các host mà người dùng đã truy cập (ssh vào) trong ~/.ssh/known_hosts để kiểm tra giá trị này vào lần SSH sau (ngoại lệ xem cuối bài).

~/.ssh/known_hosts Contains a list of host keys for all hosts the user has logged into that are not already in the systemwide list of known host keys.

Ví dụ 2 dòng trong file known_hosts:

github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz

Mỗi dòng có định dạng: tên host đã dùng khi kết nối, loại key, và dạng base64 của public host key của host được ssh vào, ngoài ra có thể có comment, mỗi phần cách nhau bởi dấu khoảng trắng (space).

Each line in these files contains the following fields: marker (optional), hostnames, keytype, base64-encoded key, comment.

Host key

Mỗi OpenSSH server đều tự sinh (nhiều) cặp host key trong lần đầu chạy. Các cặp host key này nằm trong /etc/ssh/ssh_host*

$ ls -la /etc/ssh/ssh_host_*                                                                                                                                                                    
-rw------- 1 root root  505 Apr 27 21:18 /etc/ssh/ssh_host_ecdsa_key
-rw-r--r-- 1 root root  173 Apr 27 21:18 /etc/ssh/ssh_host_ecdsa_key.pub
-rw------- 1 root root  399 Apr 27 21:18 /etc/ssh/ssh_host_ed25519_key
-rw-r--r-- 1 root root   93 Apr 27 21:18 /etc/ssh/ssh_host_ed25519_key.pub
-rw------- 1 root root 2590 Apr 27 21:18 /etc/ssh/ssh_host_rsa_key
-rw-r--r-- 1 root root  565 Apr 27 21:18 /etc/ssh/ssh_host_rsa_key.pub

Nhiều hệ thống tạo các host key cho các thuật toán như rsa, ecdsa và ed25519. Khi ssh vào một host, ssh server chọn rồi hiển thị một pub key cho ssh client, client sẽ ghi nội dung này vào file known_hosts.

ssh 127.0.0.1                                                                                                                                                                                 
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ED25519 key fingerprint is: SHA256:pyugW2eDd0Zdb0cpDUNXQltJQSsIudBGrRgnedlL5lg
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:4: localhost
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '127.0.0.1' (ED25519) to the list of known hosts.

Tương tự nếu ssh localhost, sẽ có thêm một dòng trong file known_hosts:

127.0.0.1 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENupkYnPGH10sMOBKaABnYSfVl8KSNo2bO2B83QYRWJ
localhost ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENupkYnPGH10sMOBKaABnYSfVl8KSNo2bO2B83QYRWJ

2 dòng này dù có hostname/IP khác nhau (của cùng 1 server), nhưng cùng một public host key (nội dung /etc/ssh/ssh_host_ed25519_key.pub trên server).

Dùng hash để ẩn hostname và địa chỉ IP

Để tránh bị lộ các hostname/IP mà người dùng đã truy cập, trong trường hợp bị hacker chiếm quyền SSH vẫn không biết có thể SSH vào các địa chỉ nào, OpenSSH có thêm tính năng cho phép thay domain/IP ở phần đầu mỗi dòng thành dạng hash.

Chạy ssh-keygen -H để thực hiện hash các tên hostname/IP trong file known_hosts, và chuyển file ban đầu tới file có thêm đuôi .old:

-H Hash a known_hosts file. This replaces all hostnames and addresses with hashed representations within the specified file; the original content is moved to a file with a .old suffix.

$ ssh-keygen -H -f ~/.ssh/known_hosts                                                                                                                                                         
/home/hvn/.ssh/known_hosts updated.
Original contents retained as /home/hvn/.ssh/known_hosts.old
WARNING: /home/hvn/.ssh/known_hosts.old contains unhashed entries
Delete this file to ensure privacy of hostnames

Ví dụ sau khi hash:

|1|1yLv1MYg3e3HDK+8IKFfJhtCNCw=|CrQpRw2fNTP3SZJ+povIK/ko2Ys= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
|1|om6Oxzr+1drcW1yy15GKEjwZe/o=|Nxqyg7tgIHut5QsMJWbuwDLbRis= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=

Định dạng sau khi hash: |1|SALT|hashed_hostname|key_type b64_public_host_key comment. Với |1| là đánh dấu phiên bản cho HMAC-SHA1.

Tự tính hashed hostname bằng Python

Hashed hostname được tính bằng hmac_sha1(SALT, hostname), với SALT được sinh ngẫu nhiên khi tính.

Trong ví dụ: |1|1yLv1MYg3e3HDK+8IKFfJhtCNCw=|CrQpRw2fNTP3SZJ+povIK/ko2Ys= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl Salt đã được biểu diễn ở dạng base64, giá trị: 1yLv1MYg3e3HDK+8IKFfJhtCNCw=. Code Python:

import hmac
import hashlib
import base64

def calculate_ssh_hash(hostname, salt_base64):
    # 1. Decode the salt from base64
    salt = base64.b64decode(salt_base64)

    # 2. Compute HMAC-SHA1(salt, hostname)
    # The hostname must be encoded as bytes
    hasher = hmac.new(salt, hostname.encode('utf-8'), hashlib.sha1)

    # 3. Base64 encode the result
    digest = hasher.digest()
    return base64.b64encode(digest).decode('utf-8')

# Example Usage
# Salt found in known_hosts (after |1|)
salt_b64 = "1yLv1MYg3e3HDK+8IKFfJhtCNCw="
hostname = "github.com"

hashed_host = calculate_ssh_hash(hostname, salt_b64)
print(f"Hashed Hostname: {hashed_host}")
# python3 gen.py                                                                                                                                                                    
Hashed Hostname: CrQpRw2fNTP3SZJ+povIK/ko2Ys=

Hoặc dùng một câu lệnh openssl:

$ echo -n "github.com" | openssl sha1 -binary -mac HMAC -macopt hexkey:$(echo "1yLv1MYg3e3HDK+8IKFfJhtCNCw=" | base64 --decode | xxd -p) | base64
CrQpRw2fNTP3SZJ+povIK/ko2Ys=

Đọc code ssh-keygen xem code C để tính hashed host key

/* XXX hmac is too easy to dictionary attack; use bcrypt? */
char *
host_hash(const char *host, const char *name_from_hostfile, u_int src_len)
{
    struct ssh_hmac_ctx *ctx;
    u_char salt[256], result[256];
    char uu_salt[512], uu_result[512];
    char *encoded = NULL;
    u_int len;

    len = ssh_digest_bytes(SSH_DIGEST_SHA1);

    if (name_from_hostfile == NULL) {
        /* Create new salt */
        arc4random_buf(salt, len);
    } else {
        /* Extract salt from known host entry */
        if (extract_salt(name_from_hostfile, src_len, salt,
            sizeof(salt)) == -1)
            return (NULL);
    }

    if ((ctx = ssh_hmac_start(SSH_DIGEST_SHA1)) == NULL ||
        ssh_hmac_init(ctx, salt, len) < 0 ||
        ssh_hmac_update(ctx, host, strlen(host)) < 0 ||
        ssh_hmac_final(ctx, result, sizeof(result)))
        fatal_f("ssh_hmac failed");
    ssh_hmac_free(ctx);

    if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 ||
        __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1)
        fatal_f("__b64_ntop failed");
    xasprintf(&encoded, "%s%s%c%s", HASH_MAGIC, uu_salt, HASH_DELIM,
        uu_result);

    return (encoded);
}

openssh/openssh-portable/hostfile.c

Khi ssh không dùng known host

Câu lệnh ssh có option để chọn file known_hosts, chọn file khác sẽ khiến ssh không update file mặc định ~/.ssh/known_hosts:

ssh -o UserKnownHostsFile=/dev/null 192.168.1.15

Kết luận

Hashed hostname không dịch ngược được, nhưng khi có danh sách domain/IP để làm đầu vào, hoàn toàn có thể tính ra.

Tham khảo

Hết.

HVN at https://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺

antigravity agy failed to login, it's always DNS

On one cool mid summer not so good day, agy suddenly failed to login with error:

Got an error: token exchange failed: Post "https://oauth2.googleapis.com/token": dial tcp: lookup oauth2.googleapis.com: no such host

So it clearly is a network issue, but what?

It is NOT DNS

$ dig oauth2.googleapis.com +short
142.251.8.95

dig works, so it is NOT DNS, right?

But ping / curl fail:

$ ping -c1 oauth2.googleapis.com  -t 2
zsh: alarm      ping -c1 oauth2.googleapis.com -t 2
$ curl -v https://oauth2.googleapis.com  --max-time 5
* Resolving timed out after 5005 milliseconds
* Closing connection
curl: (28) Resolving timed out after 5005 milliseconds

And both error messages from agy and curl says "lookup"/"resolve", it must be DNS.

It must be DNS

A search for dial tcp: lookup no such host return a Go issue https://github.com/golang/go/issues/41425. AI would also very good at detect language used base on error message. agy is a Go program, so it means "it is a DNS issue hit a Go program".

The fix

Go net package https://pkg.go.dev/net#hdr-Name_Resolution writes:

The resolver decision can be overridden by setting the netdns value of the GODEBUG environment variable (see package runtime) to go or cgo, as in:

GODEBUG=netdns=go agy   # force pure Go resolver

and problem solved.

The problem

It turns out the MacOS DNS daemon failed after multiple sleep/wakeup cycles.

sudo killall -HUP mDNSResponder

would give a new fresh one and now everything is fresh, again.

why dig work?

dig/nslookup uses it own DNS resolver, other programs like ping, curl, or agy use system DNS resolver. A hanging DNS resolver would not affect dig/nslookup.

Conclusion

It's always DNS.

Ref

Hết.

HVN at https://pymi.vn and https://www.familug.org.

Ủng hộ tác giả 🍺

Vagrant ssh (với VirtualBox backend) hoạt động thế nào?

Vagrant là công cụ tuyệt vời để tạo máy ảo, với 3 câu lệnh, ta đã chui vào máy ảo mới tạo:

vagrant init
vagrant up
vagrant ssh

Bên dưới có rất nhiều "magic" xảy ra, đó là những giá trị mà Vagrant mang lại thay vì tạo máy ào bằng tay với VirtualBox/VMWare/KVM...

Vagrant ssh là câu lệnh thú vị và bất ngờ khi tìm hiểu: kết nối mạng từ máy thật (host) vào máy ảo (guest) như thế nào.

Test với máy ảo chạy fedora, không chỉnh sửa config gì sau khi vagrant init ngoài box:

  config.vm.box = "fedora/33-cloud-base"
  config.vm.box_version = "33.20201019.0"

Bật debug

Cách đơn giản để tìm hiểu 1 phần mềm bất kỳ là bật chế độ logging debug/verbose lên, vagrant không là ngoại lệ.

VAGRANT_LOG=info vagrant ssh

có các chế độ log nhiều hơn như VAGRANT_LOG=debug nhưng với info cũng đã đủ rồi:

$ VAGRANT_LOG=info vagrant ssh
 INFO global: Vagrant version: 2.2.6
 INFO global: Ruby version: 2.4.9
 INFO global: RubyGems version: 2.6.14.4
 INFO vagrant: `vagrant` invoked: ["ssh"]
...
 INFO subprocess: Starting process: ["/usr/local/bin/VBoxManage", "showvminfo", "63dfeb70-1337-4db3-84f4-4bf3514aa225"]
...
 INFO ssh: Invoking SSH: /usr/bin/ssh ["vagrant@127.0.0.1", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "Compression=yes", "-o", "DSAAuthentication=yes", "-o", "IdentitiesOnly=yes", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", "/Users/user/me/fedora/.vagrant/machines/default/virtualbox/private_key"]
Last login: Wed Feb 10 08:22:51 2021 from 10.0.2.2
[vagrant@localhost ~]$ 

vagrant ssh vào máy ảo qua lo (loopback interface), dùng 127.0.0.1 chứ không phải bất kỳ network interface nào khác.

VirtualBox networking modes

https://www.virtualbox.org/manual/ch06.html

VirtualBox có các networking modes sau:

Network Address Translation (NAT). (máy guest có thể kết nối internet)
NAT Network. (các máy guest có thể kết nôí đến nhau)
Bridged networking. (máy guest sử dụng network thật mà máy host đang dùng)
Internal networking. (???)
Host-only networking. (máy guest sẽ có địa chỉ IP mà máy host có thể truy cập)

NAT

Mặc định, vagrant sẽ dùng NAT, máy guest sẽ có thể kết nối internet, vagrant sẽ cấu hình port forwarding, từ 1 port nào đó trên máy host, tới port 22 trên máy guest, để có thể ssh: vagrant@127.0.0.1 -p 2222...

Port forwarding

Khi cần chạy 1 service mà muốn các máy trong mạng đều truy cập được, máy ảo cần thêm 1 interface ở mode: bridge, nó sẽ hoạt động như 1 máy thật trong mạng, với IP của mạng thật.

Khi cần chạy 1 service mà muốn máy host truy cập được, có thể forward port với NAT nói trên, hoặc tạo 1 host-only interface.

SSH Key

Một Vagrant box tải từ vagrant cloud sẽ đảm bảo nó có sẵn user vagrant, cho phép ssh qua 1 cặp SSH key cung cấp sẵn. Sau lần đầu ssh vào, vagrant sẽ sinh 1 cặp key mới và thay thế cặp key mặc định này. https://www.vagrantup.com/docs/boxes/base#vagrant-user

~/me/fedora/.vagrant/machines/default/virtualbox/private_key

Toàn bộ thông tin này có trong output bình thường của lệnh vagrant up khi chạy lần đầu:

$ vagrant up;                                                    [0]
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'generic/openbsd6'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'generic/openbsd6' version '2.0.6' is up to date...
==> default: A newer version of the box 'generic/openbsd6' for provider 'virtualbox' is
==> default: available! You currently have version '2.0.6'. The latest is version
==> default: '3.2.4'. Run `vagrant box update` to update.
==> default: Setting the name of the VM: openbsd_default_1613198270097_41776
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2200 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!

Kết luận

Với những hiểu biết này, ta đã có thể tạo các máy ảo và dùng như vagrant, hay thậm chí tạo các vagrant box để tự dùng.

https://www.vagrantup.com/docs/boxes/base

Xem thêm

Hướng dẫn vagrant cơ bản tại https://www.familug.org/2020/02/vagrant.html