TL;DR
Lua-nginx-module
を利用することで、特別なバックエンドサーバを用意せずとも、プライベートなオブジェクトにアクセス可能になります。
事前に下記を用意して下さい。
- 対象となるオブジェクトを格納しているバケットへアクセス可能なアクセスキー
- 上記アクセスキーと対になるシークレットアクセスキー
アクセスキー、シークレットアクセスキーの発行は、さくらのオブジェクトストレージコンパネ>パーミッション>アクセスキーの発行 から行うことができます。
Nginxのインストール
私はFreeBSDを利用しているので、パッケージ管理ツール等ではインストールせず、下記のようにnginxのソースコードをダウンロードし、ビルド及びインストールしました。
パッケージ管理ツールを利用する場合、少なくともlua mouduleの入ったnginxをインストールして下さい。
config.sh
については後述します(必要に応じてオプションを修正して下さい)。
# 関連パッケージのダウンロード
doas git clone https://github.com/openresty/lua-nginx-module.git
doas git clone https://github.com/vision5/ngx_devel_kit.git
doas git clone https://github.com/nbs-system/naxsi.git
# Nginxソースのダウンロード
doas wget http://nginx.org/download/nginx-1.21.4.tar.gz
doas tar -xzf nginx-1.21.4.tar.gz
# インストール
cd nginx-1.21.4/
doas sh config.sh
doas make && doas make install
config.sh
はnginxのビルドに必要なオプションを自分なりに設定したのもので、下記にその内容を示します。
利用する環境に合わせて、オプションを変更して下さい。
#!/bin/sh
export LUAJIT_LIB=/usr/local/lib
# Not use at 20211215.
#export LUAJIT_INC=/usr/local/include/luajit-2.0
export LUAJIT_INC=/usr/local/include/luajit-2.1
./configure \
--prefix=/usr/local/etc/nginx \
--sbin-path=/usr/local/sbin/nginx \
--modules-path=/usr/local/libexe/nginx \
--conf-path=/usr/local/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/tmp/nginx/client_body_temp \
--http-proxy-temp-path=/var/tmp/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/tmp/nginx/scgi_temp \
--user=www \
--group=www \
--with-http_ssl_module \
--with-openssl=/usr/local/misc/openssl \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module \
--with-http_perl_module=dynamic \
--with-threads \
--with-stream \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-http_slice_module \
--with-mail \
--with-mail_ssl_module \
--with-file-aio \
--with-http_v2_module \
--with-google_perftools_module \
--with-cc-opt='-I /usr/local/include' \
--with-ld-opt='-L /usr/local/lib' \
--add-module=../ngx_devel_kit \
--add-module=../lua-nginx-module \
--add-module=../naxsi/naxsi_src \
--add-module=../ngx_http_geoip2_module
Nginxの設定
以下のようなconfファイル(ここでは /usr/local/etc/ngixn/conf.d/objstorage.aintek.xyz.conf
とします)を作成し、nginxで読み込みます。
このファイルを利用する場合、下記の項目を修正する必要があります。
-
$aws_access_key
: アクセスキーの値を入力して下さい -
$aws_secret_access_key
: シークレットアクセスキーの値を入力して下さい -
$bucket
: さくらのオブジェクトストレージにあるバケット名を入力して下さい
map $sent_http_content_type $expires {
"text/html" epoch;
"text/html; charset=utf-8" epoch;
default off;
}
server {
listen 80;
server_name _;
#ssl_certificate /etc/letsencrypt/live/objectstorage.aintek.xyz/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/objectstorage.aintek.xyz/privkey.pem;
access_log /var/log/nginx/objectstorage.aintek.xyz_access.log;
error_log /var/log/nginx/objectstorage.aintek.xyz_error.log;
gzip on;
gzip_types text/plain application/xml text/css application/javascript;
gzip_min_length 1000;
location ~ /(.*) {
resolver 8.8.8.8;
expires $expires;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 1m;
proxy_connect_timeout 1m;
set $aws_access_key "AKIAIOSFODNN7EXAMPLE";
set $aws_secret_access_key "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
set $s3endpoint "s3.isk01.sakurastorage.jp"; # AWS: "s3.amazonaws.com", Sakura: "s3.isk01.sakurastorage.jp"
set $bucket "ene97036-sakura-micro-community-with-webacc"; # Your bucket name
set $region "jp-north-1"; # Bucket region
set $key $1; # Object name
# --- AWS spec
# https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request
# --- lua implementations
# https://github.com/jobteaser/lua-resty-aws-signature/blob/master/lib/resty/aws-signature.lua
# https://github.com/paragasu/lua-resty-aws-auth/blob/09cec6d94c572aa83f6ae96aa15c3f057a81030e/lib/resty/aws_auth.lua
set_by_lua_block $a {
local resty_sha256 = require 'resty.sha256'
local hmac = require 'resty.hmac'
local str = require 'resty.string'
local timestamp = tonumber(ngx.time())
local date = os.date('!%Y%m%d', timestamp)
local date_tz = os.date('!%Y%m%dT%H%M%SZ', timestamp)
local canonical_uri = "/" .. ngx.var.key
local canonical_querystring = ""
-- Create a Hashed payload before Creating a Canonical request
local h = resty_sha256.new()
h:update(ngx.var.request_body or '')
local hashed_payload = str.to_hex(h:final())
-- Debug
-- ngx.log(ngx.ERR, "HASHED_PAYLOAD: " .. hashed_payload)
local canonical_headers = "host:" .. ngx.var.bucket .. "." .. ngx.var.s3endpoint .. "\n"
.. "x-amz-content-sha256:" .. hashed_payload .. "\n"
.. "x-amz-date:" .. date_tz .. "\n"
-- Debug
-- ngx.log(ngx.ERR, "CANONICAL HEADER: " .. canonical_headers)
local signed_headers = "host;x-amz-content-sha256;x-amz-date"
-- Create a Canonical request
local canonical_request = ngx.var.request_method .. "\n"
.. canonical_uri .. "\n"
.. canonical_querystring .. "\n"
.. canonical_headers .. "\n"
.. signed_headers .. "\n"
.. hashed_payload
-- Debug
-- ngx.log(ngx.ERR, "BEFORE: canonical_request " .. canonical_request)
-- Create a String to Sign before
local algorithm = "AWS4-HMAC-SHA256"
local scope = date .. "/" .. ngx.var.region .. "/" .. "s3" .. "/aws4_request"
-- Create a Hashed canonical request for string_to_sign
local h = resty_sha256.new()
h:update(tostring(canonical_request))
local canonical_request = str.to_hex(h:final())
-- Create a String to Sign
local string_to_sign = algorithm .. "\n" .. date_tz .. "\n" .. scope .. "\n" .. canonical_request
-- Debug
-- ngx.log(ngx.ERR, "AFTER: canonical_request " .. canonical_request)
-- ngx.log(ngx.ERR, "STRING_TO_SIGN: " .. string_to_sign)
-- Calculate Signature
local k_date = hmac:new("AWS4" .. ngx.var.aws_secret_access_key, hmac.ALGOS.SHA256):final(date, false)
local k_region = hmac:new(k_date, hmac.ALGOS.SHA256):final(ngx.var.region, false)
local k_service = hmac:new(k_region, hmac.ALGOS.SHA256):final("s3", false)
local k_signing = hmac:new(k_service, hmac.ALGOS.SHA256):final("aws4_request", false)
local signature = str.to_hex(hmac:new(k_signing, hmac.ALGOS.SHA256):final(string_to_sign, false))
-- Debug
-- ngx.log(ngx.ERR, "signature " .. signature)
ngx.req.set_header("host", ngx.var.s3endpoint)
ngx.req.set_header("x-amz-content-sha256", hashed_payload)
ngx.req.set_header("x-amz-date", date_tz)
ngx.req.set_header("Authorization", "AWS4-HMAC-SHA256 Credential=" .. ngx.var.aws_access_key .. "/" .. scope .. ", SignedHeaders=" .. signed_headers .. ", Signature=" .. signature)
-- Debug
-- local debug_headers = ngx.req.get_headers()
-- local h_str = "##"
-- for k, v in pairs(debug_headers) do
-- h_str = h_str .. k .. ":" .. v .. "\t"
-- end
-- h_str = h_str .. "##"
-- ngx.log(ngx.ERR, "HEADER: " .. h_str)
}
proxy_pass https://$bucket.$s3endpoint;
}
}
Nginxの起動
doas service nginx start
リロードする場合は、下記を入力して下さい。
doas service nginx -s reload
privateなオブジェクにアクセスしてみる
こんな感じでアクセスして確認してみて下さい。
curl localhost/index-from-my-nginx-with-key.html
<html>
<head>
<title>Object storage/Aintek.xyz</title>
</head>
<body>
<h1>Object storage accessed from my nginx WITH KEY/Aintek.xyz</h1>
<p>Example!</p>
</body>
</html>
独自ドメインの設定
最後に、ご自身が持つDNSサーバのレコードとして、AレコードやCNAMEレコードを登録します。
bar.example.com. A 192.168.1.100
bar.example.com. CNAME foo.example.com.
これで、bar.example.com
というドメイン名でアクセスできます。