# AWS ALB 黏性研究
###### tags: `AWS`
> 目前 AWS ALB 黏性無法作用,在此做個深入研究
- - -
## 目前 Zucast Audio 部分
Audio 的部份,兩台主機是幾乎同時進線,就算沒有黏性也可以正常播放,因為 duration 是幾乎相同的,但是我們查看 AWS CloudFront 的 Cache statistics 就能發現,其實中間我們 Errors 還滿多的。

## AWS 說明黏性問題
參考資料:[黏性工作階段](https://docs.aws.amazon.com/zh_tw/elasticloadbalancing/latest/application/load-balancer-target-groups.html#sticky-sessions)

官方說明需要瀏覽器設定 `SameSite=None; Secure` 才能讓性發揮作用。
- - -
## Google 說明跨域問題
參考資料:[Google Chrome 對於 SameSite Cookie 的設定](https://help.salesforce.com/articleView?id=000351874&language=zh_TW&mode=1&type=1)

主要是防範 CSRF 的攻擊,所以在 80 版本後的預設設定都改為 `SameSite=Lax`,而 80 版本前預設為 `SameSite=None; Secure`,不過他的解決方式是使用 https。
- - -
## AWS ALB with https
建立 https 需要憑證,先嘗試使用 CDN 的 https 看看是否有效果。
- - -
## AWS CloudFront with https

測試頁 : https://dev-vlive.staging.zucast.com/aws/


一樣沒有效果。
### 嘗試設定 SameSite
使用 php 設定:
```php=
<?php
setcookie('cross-site-cookie', 'AWSALB', ['samesite' => 'None', 'secure' => true]);
setcookie('cross-site-cookie', 'AWSALBCORS', ['samesite' => 'None', 'secure' => true]);
?>
```

再來看看黏性是否有發揮作用 ?


一樣沒有發揮作用。
- - -
## HAProxy Stick
HAProxy 的 Cookie 黏性不用 `SameSite=None; Secure` 也可以有黏性。


- - -
## Nginx + SameSite
嘗試在 Nginx 的 response header 中加入 `SameSite=None; secure`,看是否能有黏性。
```
location ~* \.ts$ {
expires 20s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
add_header Set-Cookie 'SameSite=None; secure';
}
location ~* \.m3u8$ {
expires 1s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
add_header Set-Cookie 'SameSite=None; secure';
}
```


沒有效果。
```
location ~* \.ts$ {
expires 20s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
add_header Set-Cookie 'cross-site-cookie=AWSALB; SameSite=None; secure';
add_header Set-Cookie 'cross-site-cookie=AWSALBCORS; SameSite=None; secure';
}
location ~* \.m3u8$ {
expires 1s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
add_header Set-Cookie 'cross-site-cookie=AWSALB; SameSite=None; secure';
add_header Set-Cookie 'cross-site-cookie=AWSALBCORS; SameSite=None; secure';
}
```


沒有效果。
```
location ~* \.ts$ {
expires 20s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
proxy_cookie_path / "/; secure; SameSite=None";
}
location ~* \.m3u8$ {
expires 1s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
proxy_cookie_path / "/; secure; SameSite=None";
}
```
參考資料 : https://www.cnblogs.com/yyfh/p/13532689.html


```
location / {
proxy_cookie_path / "/; secure; SameSite=None";
}
location ~* \.ts$ {
expires 20s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
}
location ~* \.m3u8$ {
expires 1s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
}
```


```
location / {
proxy_cookie_path ~^/(.+)$ "/$1; secure; SameSite=none";
}
location ~* \.ts$ {
expires 20s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
}
location ~* \.m3u8$ {
expires 1s;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=01;
}
```


- - -
## 比較 videojs 和 VLC 差異
我們把 Nginx 的 access log 打開,並加入 cookie 來查看 VLC 和 videojs 的差異。
```
http {
log_format main
'[$time_local] - $remote_addr:$remote_port - $remote_user - $upstream_addr $upstream_status $upstream_response_time - '
'"$request" $status $bytes_sent $request_time '
'"$http_referer" - "$http_user_agent" '
'"$http_cookie"';
access_log logs/item_access.log main;
}
```
從 VLC 播放時的 log:
```
[26/Nov/2020:06:25:03 +0000] - 172.31.16.83:51146 - - - - - - - "GET /live/ch01-TQzEJa/index.m3u8 HTTP/1.1" 206 854 0.000 "-" - "Amazon CloudFront" "AWSALB=rP1SBs+ZxqCOnpn3atDOuTeDcJNEH+cShG0eoJ/fe3L4nnkcf9d+WY9gVTTZYKo+A9Qd4tQo+89Lp5XHDgGGAJDiBZLf5mi3KWwusWPwV53gkKXdi1E6zJRZ9Zhu; AWSALBCORS=rP1SBs+ZxqCOnpn3atDOuTeDcJNEH+cShG0eoJ/fe3L4nnkcf9d+WY9gVTTZYKo+A9Qd4tQo+89Lp5XHDgGGAJDiBZLf5mi3KWwusWPwV53gkKXdi1E6zJRZ9Zhu"
[26/Nov/2020:06:25:03 +0000] - 172.31.7.67:50462 - - - - - - - "GET /live/ch01-TQzEJa/index77.ts HTTP/1.1" 200 7161 0.000 "-" - "Amazon CloudFront" "AWSALB=L3DCYzbbvzGOiZeuw9tZvubFMJ/UOwlxadqcenQxudGnY28DG5pqX1EwjVuA9RvjOcpqpfsGXkimpGnT6BCq/E0lSrNLlCRpu6uc5cI0kofCoDIG5xBRNk2FIEqy; AWSALBCORS=L3DCYzbbvzGOiZeuw9tZvubFMJ/UOwlxadqcenQxudGnY28DG5pqX1EwjVuA9RvjOcpqpfsGXkimpGnT6BCq/E0lSrNLlCRpu6uc5cI0kofCoDIG5xBRNk2FIEqy"
[26/Nov/2020:06:25:03 +0000] - 172.31.7.67:50462 - - - - - - - "GET /live/ch01-TQzEJa/index.m3u8 HTTP/1.1" 206 854 0.000 "-" - "Amazon CloudFront" "AWSALB=QbIEJrV6rlP3nWNPKPmD963lX3Dbhi04gX9tO5+iSrIVBgQlHUj3eHPbgpwlVzQq3Ul7Xa4/WQ0MIzSHGN2n24IsjbeEjmuYax3DG5vmg+oEBmI0b5rAIz9ZIkXr; AWSALBCORS=QbIEJrV6rlP3nWNPKPmD963lX3Dbhi04gX9tO5+iSrIVBgQlHUj3eHPbgpwlVzQq3Ul7Xa4/WQ0MIzSHGN2n24IsjbeEjmuYax3DG5vmg+oEBmI0b5rAIz9ZIkXr"
```
從 videojs 播放時的 log:
```
[26/Nov/2020:06:16:34 +0000] - 172.31.7.67:50042 - - - - - - - "GET /live/ch01-TQzEJa/index.m3u8 HTTP/1.1" 200 857 0.000 "-" - "Amazon CloudFront" "-"
[26/Nov/2020:06:16:36 +0000] - 172.31.7.67:50042 - - - - - - - "GET /live/JBK4UC5d/index.m3u8 HTTP/1.1" 404 294 0.000 "-" - "Amazon CloudFront" "-"
[26/Nov/2020:06:16:37 +0000] - 172.31.7.67:50046 - - - - - - - "GET /live/ch01-TQzEJa/index1306.ts HTTP/1.1" 404 294 0.000 "-" - "Amazon CloudFront" "-"
```
從 VLC 播放都會帶 AWSALB 跟 AWSALBCORS 的 cookie ,而 videojs 都沒有,所以我們研究如何讓 videojs 能回傳 cookie 或許能成功。
### HAProxy 的 log
```
[26/Nov/2020:06:50:49 +0000] - 172.19.1.10:36792 - - - - - - - "GET /live/JBK4UC5d/index.m3u8 HTTP/1.1" 200 876 0.000 "http://localhost/" - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36" "-"
[26/Nov/2020:06:50:49 +0000] - 172.19.1.10:36792 - - - - - - - "GET /live/JBK4UC5d/174.ts HTTP/1.1" 200 331028 0.000 "http://localhost/" - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36" "-"
[26/Nov/2020:06:50:50 +0000] - 172.19.1.10:36792 - - - - - - - "GET /live/JBK4UC5d/index.m3u8 HTTP/1.1" 200 876 0.000 "http://localhost/" - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36" "-"
```
HAProxy 開啟黏性,但是沒有回傳 cookie 也能有黏性。
### Nginx load balancer 的 log
```
[26/Nov/2020:06:50:04 +0000] - 172.19.0.3:55206 - - - - - - - "GET /live/JBK4UC5d/130.ts HTTP/1.0" 404 1129 0.000 "http://localhost/" - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36" "dtCookie=v_4_srv_6_sn_77DD1D41574AFE737D4CCFC0AE1E0B53_perc_100000_ol_0_mul_1"
[26/Nov/2020:06:50:05 +0000] - 172.19.0.3:55214 - - - - - - - "GET /live/JBK4UC5d/131.ts HTTP/1.0" 404 1130 0.000 "http://localhost/" - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36" "dtCookie=v_4_srv_6_sn_E7196F0E7A2C00A023C5FF1294503A10_perc_100000_ol_0_mul_1"
[26/Nov/2020:06:50:05 +0000] - 172.19.0.3:55224 - - - - - - - "GET /live/JBK4UC5d/131.ts HTTP/1.0" 404 1129 0.000 "http://localhost/" - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36" "dtCookie=v_4_srv_7_sn_DFAD825FAAFD12F3292C0B2330D7D008_perc_100000_ol_0_mul_1"
```
Nginx 沒有開啟黏性也會回傳 cookie,不過有回傳也沒有黏性。
- - -
## 解決方式
在 videojs 使用 withCredentials:
```
player = videojs("acom-player", {
playsinline: true,
controls: true,
autoplay: true,
preload: 'auto',
liveui: true,
isAudio: true,
html5: {
hls: {
withCredentials: true,
overrideNative: true
}
}
});
```
此時 Nginx 的部分就需要指定 Access-Control-Allow-Origin:
```
location ~* \.ts$ {
expires 20s;
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=02;
}
location ~* \.m3u8$ {
expires 1s;
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Set-Cookie ass=02;
}
```
這樣我們再查看 access log :
```
[26/Nov/2020:07:37:58 +0000] - 172.31.16.83:55154 - - - - - - - "GET /live/ch01-TQzEJa/index388.ts HTTP/1.1" 200 7572 0.000 "-" - "Amazon CloudFront" "AWSALB=zfMgTd8dLp3DLc/hUP5gDcZT7UFalFE+4oKxGiCtEdGQ4WHruxcE4S27/jItRYgkZY+0GKk9J4IsF9/8lBnYJCL7ZUgoBlK0eyxmpTGhWxVV9bJh3stm8nWrBbmc"
[26/Nov/2020:07:37:58 +0000] - 172.31.7.67:54598 - - - - - - - "GET /live/ch01-TQzEJa/index389.ts HTTP/1.1" 200 7196 0.000 "-" - "Amazon CloudFront" "AWSALB=hfFZoLagDJXHvoAJM9doDGN/9boObgGSb1UNYnv39D8c4CJuHk3XoHuK4iDbokVdQxef8lUTffx4mfK80zkAUvROhbN2qHU27CpxYniA2xvV3pB4ym/IPq7Y9GxH"
[26/Nov/2020:07:37:58 +0000] - 172.31.7.67:54598 - - - - - - - "GET /live/ch01-TQzEJa/index390.ts HTTP/1.1" 200 7572 0.000 "-" - "Amazon CloudFront" "AWSALB=jNdQtvWc8Vp0xss5G8ZwNCpKcONfc5H0o5V0M0lcAmcR57j461+Jr74ZUh8623p5wtc0iUdk5l9VIrKYuRipjlC+IKqhtQS6h1hBDqGtuRgKtwhbTdToxDRa9EHE"
```
再看看前端的 response header:


成功了 :+1:
- - -
## 結論
主要還是 CORS 規範比較嚴謹,一開始我們做 videojs 並沒有設置 `withCredentials`,所以 videojs 並不會回傳 Cookie,但當我們要回傳 Cookie 時,在 Nginx 端就需要設置 `Access-Control-Allow-Credentials=true` ,當 `Access-Control-Allow-Credentials=true` 時就不能把 `Access-Control-Allow-Origin` 設為 `'*'`,這樣就不符合規範,所以我們只能給定一個 domain,或是使用 `$http_origin` 讓所有 domain 都可以使用。
### 目前 staging 的設定:
1. 在 dev-vlive.staging.zucast.com 跨域存取 live.staging.zucast.com 可以成功回傳 cookie。
2. 在 dev-vlive.staging.zucast.com 跨域存取 staging-alb-802874310.ap-southeast-1.elb.amazonaws.com 無法回傳 cookie。
***※ 網站需要使用相似的 domain 才能真正將 cookie 回傳到 server。***
參考資料:
https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS
https://kknews.cc/zh-tw/code/romkr54.html