2 điểm bởi GN⁺ 2024-09-26 | 1 bình luận | Chia sẻ qua WhatsApp
  • BlogTech là bộ công cụ dựa trên C để quản lý các website quy mô nhỏ đến trung bình, cung cấp HTTPS, virtual host, quản lý chứng chỉ tự động dựa trên ACME và client quản lý máy chủ từ xa
  • Hiện đang ở giai đoạn thử nghiệm, với phiên bản 0.4.x đã được dùng để vận hành trong ` vài tuần nhưng chưa được xem là bản phát hành ổn định
  • Máy chủ chạy trên Linux và Windows nhưng HTTPS chỉ được hỗ trợ trên Linux; trên Windows cả client và server chỉ dùng được HTTP
  • Quản lý tệp từ xa được thực hiện bằng --put, --get, --delete, và các yêu cầu thay đổi tài nguyên được xác thực bằng chữ ký HMAC/SHA256 dựa trên khóa bí mật dùng chung
  • Trong môi trường vận hành cần máy chủ HTTP ở cổng 80 và bản ghi DNS cho tên miền; khi bật ACME, tệp chứng chỉ và khóa riêng sẽ được tạo tự động và lỗi sẽ được ghi vào acme.log

Mục đích và trạng thái hiện tại của BlogTech

  • BlogTech là bộ công cụ để quản lý các website quy mô nhỏ đến trung bình
  • Các tính năng được hỗ trợ gồm:
    • HTTPS

    • Virtual host

      • Quản lý chứng chỉ tự động qua ACME
      • Client để quản trị máy chủ từ xa
      • Vẫn đang ở giai đoạn thử nghiệm
      • Có trường hợp dùng phiên bản 0.4.x để phục vụ https://coz.is/ thành công trong vài tuần
      • Phiên bản một tệp trước đây nằm trong nhánh single_file

Build và chạy cơ bản

  • BlogTech chạy trên Linux và Windows
    • Build trên Linux cần thư viện phát triển OpenSSL và gcc
    • Build trên Windows cần clang
  • Việc build được thực hiện bằng script theo từng nền tảng
    • Linux: ./build.sh
    • Windows: .\build.bat
  • Kết quả build như sau
    • Linux: blogtech
    • Windows: blogtech.exe
  • Trên Linux có thể cài đặt bằng ./install.sh
  • Ví dụ chạy máy chủ cơ bản là tạo thư mục docroot rồi chỉ định --serve, --document-root=docroot, --skip-auth-check
  • Máy chủ HTTP mặc định lắng nghe tại 127.0.0.1:8080 và phục vụ nội dung trong docroot
  • Khi docroot trống, truy cập http://127.0.0.1:8080/ sẽ trả về 404

Quản lý tệp từ xa và xác thực

  • BlogTech có thể tải tệp lên máy chủ ở chế độ client
    • Tải tệp lên bằng --put
    • Tải tệp từ xa xuống bằng --get
    • Xóa tệp từ xa bằng --delete
  • Dù chạy client và server trên cùng một máy, cách hoạt động với server trên máy từ xa cũng giống như vậy
  • Các yêu cầu thay đổi tài nguyên được ký số bằng HMAC/SHA256
  • Client và server phải chia sẻ cùng một khóa bí mật, theo quy ước được lưu trong tệp admin.pwd
  • Tệp mật khẩu được chỉ định bằng tùy chọn --auth-password-file
  • Nếu không có tệp mật khẩu thì mọi yêu cầu cần xác thực sẽ bị từ chối
  • Mật khẩu rỗng cũng bị từ chối
  • Trong quá trình phát triển, có thể dùng --skip-auth-check để coi mọi yêu cầu là đã được xác thực
    • Khi dùng tùy chọn này, --auth-password-file sẽ bị bỏ qua dù có được chỉ định
    • Cần dùng cẩn thận
  • Header xác thực và điều kiện phát lại

    • Yêu cầu HTTP đã xác thực sẽ bao gồm các header X-BlogTech-
    • X-BlogTech-Nonce: token do client chọn ngẫu nhiên
    • X-BlogTech-Timestamp: UNIX timestamp tại thời điểm yêu cầu được ký lần đầu
    • X-BlogTech-Expire: thời gian tính bằng giây mà yêu cầu còn hiệu lực kể từ lúc ký
    • X-BlogTech-Signature: giá trị HMAC của thông tin yêu cầu được mã hóa Base64
    • Chữ ký được tạo bằng cách dựng chuỗi yêu cầu đã chuẩn hóa, sau đó tính HMAC/SHA256 với mật khẩu xác thực làm khóa, rồi mã hóa kết quả sang Base64 có kèm padding
    • Nonce mà server đã thấy sẽ được lưu trong bộ nhớ
    • Khi server được tải lại, các nonce trước đó sẽ bị quên nên các yêu cầu chưa hết hạn có thể bị phát lại

Virtual host và ánh xạ thư mục

  • BlogTech có thể lưu trữ nhiều website trên cùng một máy chủ
  • Tên miền được cấu hình bằng cách chỉ định tùy chọn --domain nhiều lần
  • BlogTech tạo các thư mục theo từng tên miền bên trong document root
    • default
    • websiteA.com
    • websiteB.com
  • Yêu cầu đi vào một host cụ thể sẽ tham chiếu tới thư mục gắn với host đó
  • Những yêu cầu không gắn với thư mục cụ thể sẽ tham chiếu tới thư mục default
  • Trong ví dụ, các tệp được tải lên websiteA.com, websiteB.com, other.com sẽ lần lượt được lưu vào thư mục tên miền tương ứng hoặc thư mục default

HTTPS và cấu hình chứng chỉ

  • HTTPS chỉ được hỗ trợ trên Linux
  • Lý do là thư viện HTTP nền tảng cHTTP triển khai HTTPS bằng OpenSSL
  • Trên Windows, cả client và server chỉ có thể dùng HTTP
  • Để bật HTTPS cần các tùy chọn sau
    • --https-enabled
    • --cert-file
    • --cert-key-file
  • Địa chỉ lắng nghe HTTPS mặc định là 127.0.0.1:8443
  • Có thể đổi địa chỉ và cổng lắng nghe bằng các tùy chọn sau
    • --https-addr=<addr>
    • --https-port=<port>
  • Trong quá trình phát triển, có thể tạo chứng chỉ tự ký bằng lệnh OpenSSL
  • Trình duyệt không cho phép duyệt máy chủ HTTPS dùng chứng chỉ tự ký
  • cURL cần dùng cờ --insecure mới có thể truy cập máy chủ dùng chứng chỉ tự ký
  • Nếu cần nhiều chứng chỉ, có thể truyền thêm --extra-cert
    • Chứng chỉ mặc định được cung cấp qua --cert-file, --cert-key-file
    • Chứng chỉ bổ sung được dùng khi client yêu cầu một tên miền cụ thể
  • Các tùy chọn dòng lệnh dài có thể được chuyển sang tệp cấu hình

Cấp chứng chỉ tự động dựa trên ACME

  • Giao thức ACME cho phép máy chủ web tự động yêu cầu cấp chứng chỉ từ tổ chức chứng thực
  • BlogTech hỗ trợ luồng khởi động ở chế độ HTTPS không có chứng chỉ trước, rồi tạo chứng chỉ sau đó
  • Trong môi trường vận hành cần các điều kiện sau
    • Chạy máy chủ HTTP trên cổng 80
    • Tạo bản ghi DNS liên kết tên miền với máy đang chạy server
  • ACME được bật bằng cách chỉ định --acme-enabled cùng với các tùy chọn HTTPS
  • Khác với chế độ HTTPS thông thường, ở chế độ ACME hệ thống giả định chứng chỉ chưa tồn tại
  • Để tạo chứng chỉ cần các giá trị sau
    • --acme-domain
    • --acme-email
    • --acme-country
    • --acme-organization
    • --acme-agree-tos
  • Nếu xử lý thành công sẽ tạo các tệp sau
    • acme_key.pem: khóa bí mật gắn với tài khoản ACME
    • cert.pem: chứng chỉ đã cấp hoặc tệp được chỉ định bằng --cert-file
    • key.pem: khóa riêng gắn với chứng chỉ hoặc tệp được chỉ định bằng --cert-key-file
  • Nếu có lỗi, thông báo sẽ được ghi vào acme.log
  • Có thể chỉ định --acme-domain nhiều lần để xử lý nhiều tên miền bằng ACME
  • Chứng chỉ kết quả sẽ bao gồm tất cả các tên miền đã chỉ định

Kiểm thử ACME client

  • Để kiểm thử ACME client trên Linux, cần cài Docker và clone Pebble ACME server
  • Trong docker-compose.yml, thêm mục extra_hosts để liên kết tên miền thử nghiệm với host-gateway
  • Trong /etc/hosts, ánh xạ tên miền thử nghiệm tới 127.0.0.1
  • Khởi động Pebble bằng docker compose up
  • Chạy instance thử nghiệm của BlogTech bằng ./blogtech -s --config=misc/pebble_blogtech.conf
  • Tương tác với ACME server sẽ được in ra stdout
  • Nếu thành công, acme_key.pem, cert.pem, key.pem sẽ được tạo
  • Có thể kiểm thử gia hạn chứng chỉ bằng cách thay đổi giá trị validityPeriod trong pebble/test/config/pebble-config.json
  • Dùng tùy chọn --acme-force-renewal-period=<maximum duration in ms> để buộc gia hạn ngay cả khi chứng chỉ chưa hết hạn

Tệp cấu hình và nạp tự động

  • BlogTech có thể chuyển số lượng bất kỳ đối số dòng lệnh nào sang tệp cấu hình
  • Ví dụ có thể ghi các tùy chọn liên quan đến HTTPS và ACME vào blogtech_server.conf, rồi chạy bằng --config=blogtech_server.conf
  • Nếu tên tệp cấu hình chính xác là blogtech.conf, BlogTech sẽ tự động nạp
  • Khi đó có thể chạy như ./blogtech --serve
  • Để bỏ qua việc nạp tệp cấu hình ngầm, dùng --no-config

Nhật ký crash và daemon systemd

  • Nếu BlogTech bị crash ở chế độ server, tệp crash.bin sẽ được tạo
  • Ở lần khởi động server tiếp theo, crash.bin sẽ được chuyển thành crash.log là stack trace có thể đọc được
  • Quá trình chuyển địa chỉ sang tên symbol hoặc số dòng có thể hơi thiếu ổn định
  • BlogTech có thể được cài dưới dạng daemon systemd
  • Ví dụ blogtech.service chạy /root/blogtech/blogtech -s, tự khởi động lại khi lỗi, và đặt thư mục làm việc là /root/blogtech/
  • Các tùy chọn server sẽ được tự động nạp từ /root/blogtech/blogtech.conf
  • Sau khi sao chép tệp service vào /etc/systemd/system/, hãy kích hoạt và khởi động bằng các lệnh sau
    • systemctl daemon-reload
    • systemctl enable blogtech
    • systemctl start blogtech
  • Có thể quản lý service bằng systemctl start, systemctl stop, systemctl restart, journalctl -u blogtech

1 bình luận

 
GN⁺ 2024-09-26
Ý kiến trên Hacker News
  • Tôi luôn thấy phần “không cần reverse proxy” khá khó hiểu
    Nếu reverse proxy không có lý do đặc biệt nào để hữu ích, tôi thường đưa thẳng các ứng dụng Jetty nhúng ra Internet mà không có lớp phía trước, và cũng không gặp vấn đề gì
    Những người phụ trách hạ tầng hoặc bảo mật hay hỏi tại sao không đặt nginx ở phía trước, nhưng khi hỏi lý do thì họ chỉ nói chung chung về bảo mật hoặc hiệu năng, thiếu tính cụ thể. Câu trả lời cụ thể nhất từng nghe là slow loris, nhưng từ lâu nó đã không còn là vấn đề lớn nữa
    Tôi tự hỏi liệu reverse proxy có trở thành một dạng cargo cult tập thể hay không, hay là tôi đang bỏ lỡ một lý do chính đáng, có thể áp dụng rộng rãi

    • Với tôi, reverse proxy cho phép máy chủ gốc chỉ tập trung vào phục vụ ứng dụng. Việc chấm dứt TLS, cân bằng tải, viết lại URL, và các tính năng bảo mật như WAF khi cần đều có thể xử lý ở reverse proxy, tạo ra sự tách biệt vai trò
      Nhìn chung, có thể bảo vệ máy chủ gốc và chỉ cho nó nhận lưu lượng liên quan. Khi cung cấp domain tùy chỉnh cho khách hàng, DNS của khách hàng cũng trỏ tới reverse proxy chứ không phải máy chủ gốc, nên nếu cần thay máy chủ gốc thì không phải lo khách hàng đổi DNS
    • Tôi đang chạy nhiều chương trình máy chủ trong homelab
      Mỗi chương trình chạy trên một port khác nhau, nhưng tôi muốn tất cả đều có thể truy cập công khai qua các URL khác nhau, và trên Internet chỉ muốn mở port 443
      Tôi cũng muốn tự động gia hạn chứng chỉ TLS cho từng domain. Các yêu cầu trên cần reverse proxy, và Caddy làm được cả hai
      Nếu chỉ vận hành một máy chủ duy nhất và máy chủ đó tự chấm dứt TLS, thì reverse proxy không nhất thiết cần thiết
    • Trong phần lớn triển khai, tác động hiệu năng của reverse proxy là không đáng kể, và cấu hình cũng đã được chuẩn bị sẵn
      Vì có thể thêm chấm dứt TLS, viết lại URL và các tác vụ khác sau này mà không tốn nhiều công sức, tôi dùng nó như một thói quen, và đến nay thói quen đó đã đem lại lợi ích
    • Nếu trả lời theo hướng áp dụng cho nhiều loại máy chủ đặt trước ứng dụng web, thì có vài cách mà thiết bị bổ sung có thể giúp cả về bảo mật lẫn hiệu năng
      Về bảo mật, trong toàn bộ hệ thống ta muốn dùng ít mã nhất có thể và một hệ điều hành vững chắc. Ứng dụng dạng proxy có thể đơn giản hơn nhiều so với máy chủ web/ứng dụng, và cũng có thể lọc lưu lượng đi vào, kiểm tra đầu vào, hoặc chuyển nó sang dạng có thể phân tích cú pháp an toàn và nhanh hơn. Nó cũng có thể chạy trên những hệ điều hành khó tấn công như OpenBSD, GenodeOS, INTEGRITY-178B
      Về tính sẵn sàng, trong nhiều trường hợp cũng an toàn hơn khi đặt cân bằng tải, giám sát và phục hồi trên các hệ thống này. Vì máy chủ ứng dụng có thể chết thường xuyên hơn
      Về hiệu năng, trước hết có lợi thế là có thể tối ưu hóa cao một ứng dụng đơn giản và tập trung. Sau đó có thể dùng CPU hoặc phần cứng tăng tốc PCI để tăng tốc nén hoặc mã hóa, thường gọi là offloading. Cấu hình có hiệu quả chi phí tốt nhất là nhiều máy chủ đa dụng được hưởng lợi từ một số ít máy chủ offloading đắt tiền. Một số hệ thống còn dùng cân bằng tải để gửi lưu lượng đến máy chủ xử lý tốt nhất, giảm việc dùng tài nguyên đắt đỏ
    • Tôi không nghĩ có một nguyên lý chung áp dụng cho mọi máy chủ
      Ở cấu hình tối thiểu thì không cần, nhưng ngay cả trong môi trường vận hành một host, nó cũng cho phép rolling release, nén, TLS, phục vụ file tĩnh nhanh và khả năng A/B testing tiềm năng
      Một lớp trung gian giữa request và máy chủ có thể khá hữu ích
  • Hay đấy. Trước đây tôi cũng từng tự viết một web server bằng C, và mã nguồn ở bên dưới. Nó cũng từng chạy một website thương mại trong một thời gian
    Thật đáng ngạc nhiên khi có thể làm một web server HTTP/1.1 nhỏ và nhẹ đến mức nào. Website thương mại đó chạy trên một máy có RAM 128MB và 1 CPU, là một hệ thống chat tương tác trên web mã nguồn đóng, phục vụ thường xuyên cho khá nhiều trường học ở Anh. Tuy nhiên đó là chuyện của 20 năm trước, khi Internet chưa thù địch như bây giờ
    Bài viết nói bot đóng vai trò như những fuzzer rất tốt, nhưng tôi vẫn nghĩ nên thử fuzzing thực sự một chút
    http://git.annexia.org/?p=rws.git;a=tree
    Các thành phần cần thiết:
    http://git.annexia.org/?p=c2lib.git;a=tree
    http://git.annexia.org/?p=pthrlib.git;a=tree

    • Nếu muốn chạy một web server cỡ này mà không phải quá lo về một Internet thù địch, Rust là một lựa chọn tốt
      Website của tôi https://blessed.rs chạy trên VM nhỏ nhất mà tôi tìm được, với RAM 256MB, nhưng thường chỉ dùng khoảng 60MB
    • Cái này trông thực dụng hơn nhiều so với web server HTTP/1.0 nhỏ và nhẹ mà tôi viết, nhưng tôi đoán rws không nhỏ và nhẹ đến mức đó: http://canonical.org/~kragen/sw/dev3/server.s http://canonical.org/~kragen/sw/dev3/httpdito-readme
      Điều thực sự khiến tôi ngạc nhiên là khi bản đồ bộ nhớ chỉ có 5 page 4KB, fork trên Linux trở nên cực kỳ nhanh
  • Đây là một dự án nhỏ tôi bắt đầu trong thời gian rảnh cho vui, và tôi nghĩ mọi người ở đây sẽ thích :)

    • Việc lướt qua các lỗi cũ của HTTP server và CVE để xem liệu chúng có thể ảnh hưởng đến mã của mình không, và có thể sửa thế nào, là một bài tập thú vị. Tự tay làm những thứ như vậy rất vui
    • Đúng lúc tôi muốn thử API đồng thời C11, và từ góc nhìn của người đến từ C++, tôi tò mò các cấu trúc đó hoạt động trong C như thế nào, nên muốn viết một thứ giống máy chủ
  • Tuyệt vời. Trước đây tôi đã nghĩ, và giờ vẫn thấy, việc dựng một dịch vụ tối thiểu bằng cách dùng API hệ thống ở mức thấp nhất như thế này thật sự rất thỏa mãn
    Gần như ma thuật vậy, nhất là khi thấy nó xử lý lưu lượng thật. Việc một poll() bình thường có thể đạt được những con số như vậy hơi đáng ngạc nhiên, nhưng có lẽ vì đã lâu rồi tôi không xử lý sự kiện hay benchmark ở mức đó
    Tôi cũng thích cách quản lý kết nối bằng hàm theo từng kết nối cùng các struct, mảng liên quan, và cả mảng file descriptor cho poll. Nó làm tôi nhớ đến cách làm trong nhiều gói mã nguồn mở nổi tiếng về thông lượng cao như nginx, Redis, memcached

    • Khi học C/C++ ở đại học, tôi có cảm giác đầu mình như muốn nổ tung. Đó là một trải nghiệm rất đặc thù và khiến người ta khiêm nhường, trong đó có một chút của những thứ tôi yêu thích như kỹ thuật, lịch sử, văn hóa, ngôn ngữ học
      Điều đó khiến tôi nghĩ rằng ai cũng nên biết và thử mọi ngôn ngữ có thể, dù là ngôn ngữ lập trình hay ngôn ngữ tự nhiên. Suy nghĩ bằng một ngôn ngữ nào đó là một trải nghiệm độc đáo. Những bối cảnh khác nhau khiến mọi thứ có cảm giác khác đi, và cuối cùng dù làm những việc tương tự, góc nhìn cũng thay đổi
      Chẳng hạn, để thật sự hiểu bản chất của Linux hay Git, bạn phải nói được ngôn ngữ đó và hiểu những sắc thái thường biến mất trong bản dịch. Cũng giống như muốn hiểu ý nghĩa chủ quan thật sự của từ “rừng” trong tiếng Nga thì phải nói và hiểu tiếng Nga
      Bối cảnh thay đổi góc nhìn, và đôi khi thay đổi mọi thứ
    • Hoàn toàn đồng ý. Và khi thật sự dùng thử thì còn thỏa mãn hơn. Giờ tôi bắt đầu tò mò cả về giao thức email
      Tôi cũng ngạc nhiên khi một poll() bình thường chịu được đến mức đó. Tôi từng nghĩ rồi sẽ phải chuyển sang epoll, nhưng poll() đang hoạt động rất tốt
    • Có vẻ mọi người quên rằng mọi abstraction tuyệt vời và hào nhoáng, về cốt lõi, cuối cùng cũng làm cùng một việc: mở socket, đọc và ghi
      Chẳng có gì mới dưới ánh mặt trời
  • Cái này cũng có thể thú vị: https://news.ycombinator.com/item?id=27431910
    Tính đến năm 2024, instance althttpd của sqlite.org xử lý hơn 500 nghìn request HTTP mỗi ngày, khoảng 5–6 request mỗi giây, phục vụ khoảng 200GB nội dung mỗi ngày, khoảng 18Mbit/s, trên một máy Linode 40 USD/tháng. Load average của máy này thường ở quanh mức 0.5. Khoảng 19% request HTTP là CGI trỏ đến nhiều kho mã nguồn Fossil

    • Bài này đã truyền cảm hứng lớn cho tôi. Nó giúp tôi nhận ra rằng những thứ như vậy thật sự có thể làm được
  • Nếu muốn viết ứng dụng C nhưng không thoải mái khi tự viết phần trực tiếp tiếp xúc với Internet công cộng, Kore là một framework khá ổn
    Nó có các tính năng tích hợp tiện lợi như quản lý chứng chỉ ACME, Pgsql, curl, websocket
    Về cơ bản bạn có thể build module để chạy và ghép lại với nhau, cũng có thể trộn Lua/Python với C
    https://kore.io/

  • Cuối cùng cũng có một website lên trang nhất mà không chết

    • Nếu phía trước có CDN thì site nào cũng làm được như vậy
      Không có ý nói dự án này không tuyệt, nhưng nếu trong môi trường vận hành bạn thật sự coi trọng điều này và chủ yếu phục vụ nội dung tĩnh, thì cứ dùng CDN là được. Nó gần như luôn có hiệu năng tốt hơn bất cứ thứ gì bạn tự viết. Chỉ là không vui bằng thôi
    • Không phải link dẫn tới GitHub sao? Bình luận này làm tôi hơi bối rối
      Dự án gọn gàng và hay, nhưng tôi nghĩ phần lớn người ta sẽ vào GitHub chứ không vào link hiển thị trang web. Tôi có bỏ sót gì không?
  • Tôi thích đoạn: “Tôi thích tự làm công cụ của mình, và hơi chán khi cứ bị bảo rằng mọi thứ phải được kiểm chứng trong thực chiến. Vậy nếu nó chết thì sao? Có bug thì sửa thôi :^)”

  • Chỉ 3.4k dòng C mà đã có server HTTP và HTTPS hoàn chỉnh? Thành thật mà nói, tôi tưởng để tuân thủ đầy đủ đặc tả thì sẽ cần nhiều hơn thế rất nhiều

    • HTTP/1.1 rất đơn giản nếu bỏ qua phần lớn đặc tả
      Chỉ cần nhận request GET và đặt Content-Length trong response là đủ cho 99% user agent. Xử lý Transfer-Encoding và header byte range cũng không làm code phình ra quá nhiều
      HTTPS chỉ là HTTP trên socket TLS, và nếu bạn không tự triển khai mã hóa thì mức abstraction đúng ra cũng chỉ đến vậy. Khá thú vị và thực ra không tệ đến thế
    • Mức này có vẻ hợp lý. httpd(8) [1] của OpenBSD, tính cả tài liệu, hiện cũng chỉ hơi dưới 15.000 dòng
      Nếu bỏ bớt vài tính năng và đặt ra một số giả định, thì việc rơi vào khoảng 5.000 dòng như dự án này cũng không có gì đáng ngạc nhiên
      Kết quả $ wc -l * là tổng cộng 14815 dòng
      [1]: https://man.openbsd.org/httpd.8
    • Trong một thử nghiệm của tôi, để cung cấp live view cho việc thu thập dữ liệu, tôi đã dùng một web server C nhúng đơn giản và nó dưới 250 dòng
      Tất nhiên tôi sẽ không đưa nó lên Internet công cộng và nó chỉ triển khai một phần rất nhỏ của HTTP/1.1, nhưng nó chạy được và chỉ cần malloc lúc khởi tạo
    • Còn vài server HTTP/1.1 khác có kích thước kiểu này: https://www.acme.com/software/thttpd/benchmarks.html
  • Tôi nhớ đến một bài thuyết trình ở Chaos Communication Congress về blog/web server viết bằng C
    Nội dung nói về việc đưa vào nhiều tính năng bảo mật như kho lưu trữ bất biến, hạ quyền, không cho blog truy cập chứng chỉ TLS, v.v.: https://www.youtube.com/watch?v=TaE28fJVPTk