Web Server 

Network 

  • Intranet
    A VM with a host-only adapter connecting to the same network of the original machine

    1. 創立Host網卡
      Imgur Create > enable DHCP server

    2. 將網卡加到VM
      VM Settings > Network
      Adapter 2 選Host-only Adapter
      Imgur

    3. FreeBSD /etc/rc.conf新增網卡

      ifconfig_em1="DHCP"
      
      reboot -n //重開機
      ifconfig  //查看網路設定
      

      會看到新的網卡em1 192.168.56.101

  • Public
    The Wireguard VPN (10.113.0.0/16)

HTTP Server 

Nginx 

Install Nginx

pkg install nginx

Enable Nginx when reboot

sudo sysrc nginx_enable=yes

啟動nginx

service nginx start

在本機browser輸入ip後會看到這個頁面
(如果要用外網ip 本機要記得先連上VPN)
Imgur

Configures 

Virtual Host 

❑ Setup a name-based virtual host
❑ Show different content when connecting using domain name/IP

  1. Get a domain name
    Imgur
  2. 自簽SSL
mkdir /usr/local/etc/nginx/ssl
sudo openssl req -x509 -nodes -day 365 \  
-newkey rsa:2048 -key out /usr/local/etc/nginx \
/ssl/nginx.key -out /usr/local/etc/nginx/ssl/nginx.crt
  1. Configuration

    /usr/local/etc/nginx/nginx.conf

    (location /是ip會看到的頁面)

    http {
        ...................................
        ...................................
        ...................................
        # Hide the server version in header
        server_tokens off;
    
        # SSL認證 
        ssl_certificate /usr/local/etc/nginx/ssl/nginx.crt;
        ssl_certificate_key /usr/local/etc/nginx/ssl/nginx.key;
    
        # Using IP
        server {
            # web聽port 80
            listen 80;  
    
            # https聽port 443、enable SSL & HTTP2
            listen 443 ssl http2;  
    
            # public vpn ip
            server_name 10.113.0.98;  
    
            location / {
                # Base on nakedhtml這個資料夾
                root    /usr/local/www/nginx/nakedhtml;  
    
                # 首頁是資料夾nakedhtml中的index.html
                index  index.html;
            }
    
            error_page   500 502 503 504  /50x.html;
                location = /50x.html {
                root   /usr/local/www/nginx-dist;
            }
        }
    
        # Using domain name
        server {
            listen	443 ssl http2;
    
            #server_name  localhost;
            # Domain name as name-based virtual host
            server_name weelian.nctu.me; 
    
            # SSL認證
            ssl_certificate /usr/local/etc/nginx/ssl/nginx.crt;
            ssl_certificate_key /usr/local/etc/nginx/ssl/nginx.key;
            #charset koi8-r;
            #access_log  logs/host.access.log  main;
            add_header Referrer-Policy "no-referrer" always;
            add_header X-Content-Type-Options "nosniff" always;
            add_header X-Download-Options "noopen" always;
            add_header X-Frame-Options "SAMEORIGIN" always;
            add_header X-Permitted-Cross-Domain-Policies "none" always;
            add_header X-Robots-Tag "none" always;
            add_header X-XSS-Protection "1; mode=block" always;
       
            # Remove X-Powered-By, which is an information leak
            fastcgi_hide_header X-Powered-By;
    
            index index.html index.php;
            root    /usr/local/www/nginx;
    
            # domain name root index
            location / {
                index  index.html index.htm;
            }
            ...................................
            ...................................
            ...................................
        }
        ...................................
        ...................................
        ...................................
    }
    

啟用HTTPS 

Enable HSTS(HTTP Strict Transport Security): 放在domain name server
Redirect HTTP to HTTPS: 加在http{}裡的最後面

http {
    ...................................    
    ...................................
    ...................................
    # Using domain name
    server {
	    listen	443 ssl http2;

        # Enable HSTS  
        add_header Strict-Transport-Security "max-age=31536000;
        includeSubDomains" always;
        ...................................
        ...................................
    }        
    ...................................
    ...................................
    ...................................
    server {
	    listen 80;
	    server_name weelian.nctu.me;
	    return 301 https://weelian.nctu.me$request_uri;
    }
}

Webpage /private 

  • When accessing from intranet, it must show after passing Basic Auth authentication with username admin and password {your-student-ID}
    Imgur
  • Access from public network should be denied (return 403)
  1. Auth Basic
    設定帳號密碼

    sudo sh -c "echo -n 'admin:' >> /usr/local/www/nginx/.htpasswd"
    sudo sh -c "openssl passwd -apr1 >> /usr/local/www/nginx/.htpasswd"
    
  2. 設定/usr/local/www/nginx/.htpasswd權限
    !正確!

    -rw-r-----  www www .htpasswd
    

    如果是原本的

    -rw-r-----  root    wheel   .htpasswd
    

    外網 -> 403 Forbidden
    內網輸入帳密後 -> An error occured

  3. nginx.conf

    ...................................
    ...................................
    ...................................    
    # Using domain name
    server {
        ...................................        
        ................................... 
        # domain name root index
        location / {
            index  index.html index.htm;
        }
        location /private {
            # allow Intranet
            allow 10.113.0.98;
            allow 192.168.56.101;
    
            # deny Internet
            deny all;
            auth_basic "Authorization Required";
            auth_basic_user_file /usr/local/www/nginx/.htpasswd;
            alias /usr/local/www/nginx/secret;
            index index.html index.htm;
        }
        ...................................
        ...................................
        ...................................
    

PHP/PHP-FPM 

Set up PHP such that access to https://{your- domain}/info-{your-student-ID}.php gives response of phpinfo()

  1. Install php ver 7.3.0

    pkg install php73 php73-mysqli
    
  2. 建立 php.ini
    建立php.ini-production 的 soft-link 給 php.ini

    cd /usr/local/etc
    sudo cp php.ini-production php.ini
    
  3. Enable PHP

    sudo sysrc php_fpm_enable=yes
    sudo service php-fpm start
    
  4. Configure nginx.conf

    upstream php-handler {
        server 127.0.0.1:9000;
        #server unix:/var/run/php/php7.2-fpm.sock;
    }
    ...................................
    ...................................
    ...................................    
    # Using domain name
    server {
        location ~ \.php$ {
            root           /usr/local/www/nginx;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            #fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }
    
  5. 定義SCRIPT_FILENAME
    預設安裝的 Nginx FastCGI 參數檔裡面沒有定義 SCRIPT_FILENAME 這個變數
    因此我們要把他加進去

    sudo vim /usr/local/etc/nginx/fastcgi_params
    

    找到SCRIPT_FILENAME

    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    
  6. info.php

    <?php
        phpinfo();
    ?>
    
  7. 改檔名

    cd /usr/local/www/nginx-dist
    sudo mv info.php info-W081001.php
    

    這樣去 https://weelian.nctu.me/info-W081001.php
    就會看到phpinfo()
    Imgur

  8. Hide PHP version in response header
    /usr/local/etc/php.ini

    expose.php=off
    

MySQL 

Prepare for NextCloud

Install MySQL 

portsnap fetch update
cd /usr/ports/databases/mysql57-server
sudo make WITH_CHARSET=utf8 install clean

Configuration 

/usr/local/etc/my.conf

Start MySQL daemon 

sudo /usr/local/etc/rc.d/mysql-server start

Login 

login mysql as root with passwd

mysql -u root mysql -p

Create Database, User 

Create a MySQL user named ‘nc’ and a database named ‘nextcloud’, which satisfies:
• The password of the user is your student ID
• This user can only login from localhost
• This user only have full privileges on database ‘nextcloud’

> create DATABASE nextcloud;
> show database;
> GRANT ALL ON nextcloud.* TO nc@localhost IDENTIFIED BY 'W081001';
> quit

Login as nc

mysql -u nc nextcloud -p

Set the transaction isolation levels to READ-COMMITED

> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITED;
> SELECT @@TX_ISOLATION;

HTTP Applications 

Basic App Router 

There are three path displaying different contents:

  • https://{your-domain}/app
    Display route: /
  • https://{your-domain}/app/{A}+{B} (A, B are integers)
    Display result: {value of A+B}
  • https://{your-domain}/app?name={string}
    Display Hello, {string}
  1. app/index.php
    <?php
        $input = $_SERVER["QUERY_STRING"];
        if(strlen($input)>0){
            $ars = explode("=",$input);
    
            if(strlen($ars[1])>0){
                echo "Hello, $ars[1]";
            }
    
            else{
                $arr = explode("+",$input);
                $sum = (int)$arr[0]+(int)$arr[1];
                echo "result: $sum";
            }
        }
        else{
            echo 'route: /';
        }
    ?>
    
  2. nginx.conf
    location /app {
        alias /usr/local/www/nginx/app;
        index index.php;
        try_files $uri $uri/ @test;
    }
    
    location @test {
                rewrite ^/app/(.*)\+(\w+)\.?.*$ /app?$1+$2; 
    }
    

WebSocket 

A sample program

  • This program crashes at client refresh, so just use it for test or demo
  • You don’t need to support HTTP2 for WebSocket connection
  • Configure your HTTP server such the program run appropriately that when accessing http(s)://{your- domain}/wsdemo
  • Let it be connected on port 443(wss://)

Original: WebSocket is on port 12345 Imgur

Goal: connect websocket on port 443 Imgur

In nginx.conf

...................................
...................................
...................................  
upstream websocket {
	server 0.0.0.0:12345;
}

# Using domain name
server {
    location /wsdemo {
    	alias /usr/local/www/nginx/wsdemo;
    	index websockets.html;
    	# proxy_pass http://websocket;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location /public {
    	proxy_pass http://websocket;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

In wsdemo/websockets.html

<html>
<body>
    <div id="root"></div>
    <script>
        # var host = 'ws://0.0.0.0:12345/websockets.php';
        var host = 'wss://weelian.nctu.me/public';
        var socket = new WebSocket(host);
        socket.onmessage = function(e) {
            document.getElementById('root').innerHTML = e.data;
        };
    </script>
</body>
</html>

In wsdemo/websockets.php

<?php

$address = '0.0.0.0';
$port = 12345;

// Create WebSocket.
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, $address, $port);
socket_listen($server);
$client = socket_accept($server);

// Send WebSocket handshake headers.
$request = socket_read($client, 5000);
preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $request, $matches);
$key = base64_encode(pack(
    'H*',
    sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
));
$headers = "HTTP/1.1 101 Switching Protocols\r\n";
$headers .= "Upgrade: websocket\r\n";
$headers .= "Connection: Upgrade\r\n";
$headers .= "Sec-WebSocket-Version: 13\r\n";
$headers .= "Sec-WebSocket-Accept: $key\r\n\r\n";
socket_write($client, $headers, strlen($headers));

// Send messages into WebSocket in a loop.
while (true) {
    sleep(1);
    $content = 'Now: ' . time();
    $response = chr(129) . chr(strlen($content)) . $content;
    socket_write($client, $response);
}

我一開始在執行socket的時候 出現Error

PHP Fatal Error: Uncaught Error: Call to undefined function socket_create() in....

後來發現是因為沒裝Socket Extension

cd /usr/ports/lang/php73-extensions
make config install clean

[x]socket

Nextcloud 

要在{domain}/nextcloud

  1. Install
    要先用pkg install 才會有完整的php-fmp

    pkg search nextcloud
    pkg install nextcloud-php-73
    

    pkg裝好的nextcloud 會在/usr/local/www
    再把它刪掉 用本來的zip替換
    改權限

    sudo chown -R www:www nextcloud
    
  2. Configuration in nginx.conf
    Google: Nextcloud in a subdir of nginx
    copy & paste in nginx.conf

    如果開{domain}/nextcloud 出現An error occured
    -> 重開php-fpm

    sudo service php-fpm restart
    

    or
    -> kill PID

    ps aux | grep php
    sudo kill {php pid}
    sudo service php-fpm start
    

    在一開始 Create user時 記得要選MySQL DB

  3. Personal Webpage

    • Each user in Nextcloud can put static(PHP is not needed) contents(img, html, css, js, etc.) in the public_html directory under their home
    • When accessing https://{your-domain}/sites/~{username}/ , it should show whatever {username} put in his public_html, with index index.html
    location /sites {
        try_files $uri $uri/ @ncsites;
    }
    location @ncsites {
        rewrite ^/sites/\~(.*) /nextcloud/data/$1/files/public_html/index.html;
    }