Hướng dẫn dưới đây giúp thiết lập CGI (Common Gateway Interface) trên Apache2 để xây dựng các ứng dụng web động bằng bất kỳ ngôn ngữ nào.

Bật module CGI trên Apache2

Cài đặt Apache2 nếu chưa cài.

$ sudo apt-get install apache2

Cho phép Module CGI hoạt động.

$ sudo a2enmod cgi
$ sudo systemctl restart apache2

Chuẩn bị Document Root cho ứng dụng CGI

Tạo một Document Root mới cho trang web, ví dụ /var/www/example.

$ sudo mkdir -p /var/www/example/cgi-bin
$ sudo chown -R www-data:www-data /var/www/example

Cấu hình VirtualHost

Giả sử tên miền bạn sẽ sử dụng là www.example.com. File cấu hình sau cho phép tất cả các ứng dụng đặt trong thư mục cgi-bin của web root có thể được khởi chạy bằng CGI.

/etc/apache2/sites-available/example.conf

Define VAR_DOMAIN   www.example.com
Define VAR_EMAIL    webmaster@example.com
Define VAR_ROOT     /var/www/example
<VirtualHost *:80>
    ServerName ${VAR_DOMAIN}
    ServerAdmin ${VAR_EMAIL}
    DocumentRoot ${VAR_ROOT}

    <IfModule mod_alias.c>
        <IfModule mod_cgi.c>
            Define VAR_CGI_BIN
        </IfModule>
        <IfModule mod_cgid.c>
            Define VAR_CGI_BIN
        </IfModule>
        <IfDefine VAR_CGI_BIN>
            ScriptAlias /cgi-bin/ ${VAR_ROOT}/cgi-bin/
            <Directory ${VAR_ROOT}/cgi-bin>
                AllowOverride None
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Require all granted
            </Directory>
        </IfDefine>
    </IfModule>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Hãy thay thế www.example.com bằng tên miền của bạn, cũng như /var/www/example bằng đường dẫn web root tương ứng.

Yêu cầu Apache2 sử dụng VirtualHost này. Tham số của a2ensite là tên của file config trên (có hoặc không có phần mở rộng .conf đều được).

$ sudo a2ensite example
$ sudo systemctl reload apache2

Thử hoạt động của CGI

Tạo một file CGI script trong thư mục /var/www/example/cgi-bin và phân quyền executable cho script.

Ví dụ, CGI script sau được viết bằng ngôn ngữ BASH sẽ in tất cả các biến môi trường và giá trị của chúng ra trình duyệt. Phần mở rộng của script là gì cũng được.

/var/www/example/cgi-bin/env-var.cgi

#!/bin/bash

echo "Content-type: text/html"
echo "" # mandatory

echo "<html>"
echo "<head>"
echo "<title>Environment Variables</title>"
echo "</head>"
echo "<body>"

echo "Environment Variables:"
echo "<pre>"
# Dump all environment variables and their values
/usr/bin/env
echo "</pre>"

echo "</body>"
echo "</html>"

exit 0

Phân quyền executable cho CGI script, nếu không sẽ gặp lỗi 500 Server Error khi truy cập.

$ sudo chmod +x /var/www/example/cgi-bin/env-var.cgi

Truy cập CGI script từ trình duyệt qua địa chỉ: http://www.example.com/cgi-bin/env-var.cgi.

Thực thi CGI dưới quyền root

Các ứng dụng CGI sẽ được thực thi dưới quyền của user www-data, là một user có quyền lực tương đối hạn chế. Để thực hiện những tác vụ có quyền lực cao như root thì có 2 hướng:

Cách 1: Điều khiển service đang chạy quyền root bằng IPC

Script CGI có điều khiển một ứng dụng Daemon khác chạy dưới quyền root thông qua một phương thức IPC (Interprocess Communication) như Pipe, UNIX domain socket v.v…

Đây là cách tương đối an toàn vì lúc này vấn đề an toàn chỉ tập trung ở Daemon và CGI Script của bạn.

Cách 2: Sử dụng suEXEC

Apache2 có một module là suExec cho phép chạy lệnh CGI dưới quyền một user khác. Dù không hỗ trợ tài khoản root, ta vẫn có thể thiết lập user đó có quyền sudo. Tham khảo cách cấu hình tại đây.

Cách 3: Cấp quyền sudo cho user www-data

Đây là cách rất nguy hiểm có thể tạo lỗ hổng bảo mật nghiêm trọng vì user www-data được sử dụng với nhiều web khác chứ không chỉ giới hạn ở app CGI của bạn.

Bạn chỉ nên giới hạn số lệnh cho phép và những lệnh đó không nên có quyền chỉnh sửa hệ thống.

Ngoài ra do ứng dụng web không hỗ trợ tương tác người dùng nên phải cấu hình sudo có thể thực thi mà không hỏi password.

$ sudo visudo

Thêm vào dòng sau trong trình sudo editor:

www-data  ALL=(ALL)  NOPASSWD: /path/to/command-1, /path/to/command-2

Với command-1 và command-2 là các lệnh bạn muốn chạy dưới quyền root với www-data, danh sách các lệnh cho phép ngăn cách bằng dấu phẩy.

Tuyệt đối ĐỪNG BAO GIỜ cấp quyền lực vô hạn cho www-data như thế này!

www-data  ALL=(ALL)  NOPASSWD:ALL