DriveToWeb のような Google Drive のホスティングサーバーを無料で構築する

DriveToWeb の代替サービスを Compute Engine に構築 Linux
このサイトはアフィリエイト広告(Amazonアソシエイト含む)を掲載しています。
スポンサーリンク

Google Drive の共有フォルダを Web サイトとして公開できる DriveToWeb(https://drv.tw)という素晴らしいサービスがあります。

しかしサービスが不安定で、肝心な時にページが見られなくて困ったことが度々ありました。

そこで今回は無料のサービスを組み合わせて、DriveToWeb と同様の Google Drive をホスティングするサーバーを自前で作ってみます。

  • ホスティングサーバー:Compute Engine(GCE)
  • ストレージ:Google Drive
  • ドメイン:DDNS Now

目指す環境は DriveToWeb と同様に静的ファイル(HTML、CSS、JS、画像など)を表示できれば良く、PHP などは動かせなくて良い事とします。

ホスティングサーバーの準備

Google Drive をホスティングするなら、同じ Google のサービスで揃えた方が気分が良いという安直な理由で Google Cloud(GCP)の Compute Engine(GCE)を選定しました。

ただし、GCE は転送量が 1GB/月 を超えると課金が発生するため、アクセス数の多いサイトや画像・動画がメインのサイトは、転送量制限の無い VPS などを使った方が無難だと思います。

Compute Engine の VM インスタンスを作成

GCP を無料枠で運用するためは、様々なポイントがあります。解説サイトを見ながら慎重に設定してください。

(2023年版) Google Cloud 無料枠 ホントに課金されない?の検証
Google Cloudには初回90日間の無料トライアルとは別に、いつでも使える「無料枠」があります。一定の範囲内で使用する分は無料になるのですが、その条件は単純明快…というわけでもないので「本当に課金されない?」と躊躇するかもしれません。...

今回は下記の設定で VM インスタンスを作成しました。

  • リージョン:us-west1 (オレゴン)
  • マシンタイプ:e2-micro
  • OS:Debian
  • バージョン:Debian GNU/Linux 12 (bookworm)
  • ブートディスクの種類:標準永続ディスク(30GB)
  • ファイアウォール:HTTP トラフィックを許可する、HTTPS トラフィックを許可する
  • 外部 IP:エフェメラル(初期設定)

予算アラートの作成

「お支払い」>「予算とアラート」から「予算を作成」し、想定外の課金があっても気づけるようにしておきましょう。

  • 名前:無料利用枠
  • 予算タイプ:指定額
  • 目標金額:100円

今回は上記内容で設定しました。

初期設定

Compute Engine に戻り、VM インスタンスに SSH 接続して初期設定を行っていきます。

システムの更新

まずはシステムを最新にアップデートしましょう。

sudo apt update
sudo apt upgrade

※ 日々のアップデートは unattended-upgrades によって自動的に行われます。

スワップファイルの作成

今回作成した e2-micro はメモリが 1GB と少ないので、スワップファイルを作成しておくと良いでしょう。

sudo mkdir /var/spool/swap
sudo dd if=/dev/zero of=/var/spool/swap/swapfile bs=1M count=1024
sudo chmod 600 /var/spool/swap/swapfile
sudo mkswap /var/spool/swap/swapfile
sudo swapon /var/spool/swap/swapfile
sudo vi /etc/fstab

fstab を編集して、サーバー起動時に読み込むようにします。

# 最終行に追加
/var/spool/swap/swapfile none swap sw 0 0

ファイアウォールの設定

GCE の無料枠に 1GB までの下りのネットワークが含まれていますが、中国とオーストラリアは例外で課金対象となります。SSH の総当たり攻撃を受けて課金が発生した例もあるようなので、ファイアウォールでブロックしておきましょう。

GCEのf1-microインスタンスを真にタダで使う方法 - Qiita
2017年3月にGoogle Cloud Platform (GCP) の価格改定があり、無料枠 (Always Free Products)も強化されました。その中でも、Google Compute Engine (GCE) の無料化が個...

今回のファイアウォールの方針

  • SSH(22)に関しては、GCE の管理画面から起動できるブラウザ版が使いやすかったので、思い切って SSH は管理画面からのアクセス(IAP)のみに制限します。
  • Google Drive を Web として公開したい相手は日本国内のみで良いので、HTTP(80)と HTTPS(443)のアクセスは国内のみに制限します。
  • その他のアクセスは全てブロックします。

nftables の設定

ファイアウォールには nftables を使います。

sudo apt install nftables

下記のサイトを参考に、日本国内の IPv4 アドレスを収集するスクリプトを作成しました。

nftablesでSSHを日本国内からの接続に限定する (CentOS 8)
はじめに以前、国別のIPアドレスリストを利用して、CentOS 7で、ipset と iptables で SSH を日本国内からの接続に限定する設定方法をまとめました。・ipsetとiptablesでSSHを日本国内からの接続に限定する ...
vi nftables_domestic.sh
#!/usr/bin/bash

LIST=/etc/nftables.domestic

wget http://nami.jp/ipv4bycc/cidr.txt.gz
gunzip cidr.txt.gz
echo "define domestic = {" > ${LIST}
for COUNTRY in 'JP'
do
    sed -n "s/^JP\t//p" cidr.txt | while read ADDRESS
    do
        echo "        ${ADDRESS}," >> ${LIST}
    done
done
echo "}" >> ${LIST}
rm cidr.txt

スクリプトを実行して、日本国内の IPv4 アドレスのリストを作成します。

chmod +x ~/nftables_domestic.sh
sudo sh ~/nftables_domestic.sh

HTTP(S) へのアクセスは、このリストに記載された IP アドレスに制限します。

また、SSH へのアクセスを IAP(Identity-Aware Proxy)に限定するためには「35.235.240.0/20」の範囲を許可すれば良さそうです。

TCP 転送での IAP の使用  |  Identity-Aware Proxy  |  Google Cloud Documentation

nftables.conf を編集して、上記のリストを読み込みます。

sudo vi /etc/nftables.conf
#!/usr/sbin/nft -f

# 現在のルールセットを消去
flush ruleset

# 日本国内の IP アドレスリストを読み込む
include "/etc/nftables.domestic"

table inet filter {
        set iap {
                type ipv4_addr
                flags interval
                # IAP の IP アドレス範囲
                elements = { 35.235.240.0/20 }
        }
        set domestic {
                type ipv4_addr
                flags interval
                elements = $domestic
        }
        chain input {
                type filter hook input priority 0; policy drop;
                # Ping floods
                ip protocol icmp icmp type echo-request limit rate 10/second accept
                # 関連・確立済みトラフィックは許可する
                ct state established,related accept
                # 不正なトラフィックは全て破棄する
                ct state invalid drop
                # ループバックインターフェイスのトラフィックは全て許可する
                iif lo accept
                # 新しいエコー要求 (ping) は許可する
                ip protocol icmp icmp type echo-request ct state new accept
                ip protocol tcp tcp flags & (fin | syn | rst | ack) == syn ct state new jump TCP
                # 他のルールによって処理されなかったトラフィックは全て拒否する
                ip protocol tcp reject with tcp reset
        }
        chain forward {
                type filter hook forward priority 0; policy drop;
        }
        chain output {
                type filter hook output priority 0; policy accept;
        }
        chain TCP {
                # SSH は IAP のみ許可
                tcp dport 22 ip saddr @iap accept
                # HTTP(S) は日本国内のみ許可
                tcp dport { 80, 443 } ip saddr @domestic accept
        }
}

nftables を起動して、ファイアウォールを有効にします。

sudo systemctl start nftables.service
sudo systemctl enable nftables.service

Google Drive のマウント

最初は Rclone を使って Google Drive とファイルを同期する方法 を考えていたのですが、Google Drive 上のフォルダを直接マウントする事ができる google-drive-ocamlfuse というものを見つけたので、今回はそれを使っていきます。

google-drive-ocamlfuse
FUSE filesystem over Google Drive

google-drive-ocamlfuse のインストール

まず最初に、google-drive-ocamlfuse をインストールするために必要な opam(OCaml Package Manager)と、関連パッケージをインストールしていきます。

sudo apt install opam fuse libfuse-dev libcurl4-gnutls-dev libgmp-dev libsqlite3-dev pkg-config zlib1g-dev

opam の初期化とアップデートをした後、google-drive-ocamlfuse をインストールします。

opam init
(out) Do you want opam to modify ~/.profile? [N/y/f]
(out) (default is 'no', use 'f' to choose a different file) N
(out) 
opam update
opam install google-drive-ocamlfuse
(out) Do you want to continue? [Y/n] y
(out) 
(out) ∗ installed google-drive-ocamlfuse.0.7.32
(out) Done.
(out) # Run eval $(opam env) to update the current shell environment

1 時間近くかかりましたが、google-drive-ocamlfuse 0.7.32 をインストールできました。

インストールに失敗した場合

~/.opam ディレクトリを削除して、opam init からやり直してください。

メモリ不足の場合は、スワップファイルを設定すると良いでしょう。

google-drive-ocamlfuse を簡単に実行できるように「~/.opam/default/bin」へのパスを通しておきましょう。

vi ~/.bashrc
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
PATH="$PATH:$HOME/.opam/default/bin"

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac
exit
(out) logout

一度ログアウトし、再び SSH にログインすると反映されます。

google-drive-ocamlfuse と Google Drive の初回連携

今回の手順の中で一番ややこしい部分になります。

有効な API とサービス

  1. Google Cloud(GCP)の「API とサービス」を開きます。
  2. API とサービスのメニューにある「ライブラリ」を開きます。
  3. API ライブラリが表示されるので、「Google Drive API」を「有効化」します。

OAuth 同意画面

次に「OAuth 同意画面」を開き、User Type を「外部」で「作成」します。

  1. OAuth 同意画面
    • アプリ名:(何でも良いので入力)
    • ユーザー サポートメール:Google アカウントのメールアドレス
    • デベロッパーの連絡先情報:(同上)
    • その他の項目:(全て空欄)
  2. スコープ
    1. 「スコープを追加または削除」をクリック
    2. フィルタに「Google Drive API」と入力して絞り込む(17 件の項目が表示される)
    3. 「…/auth/drive.metadata」、「…/auth/drive.metadata.readonly」と「.../auth/drive.readonly」の 3 つのスコープにチェックを入れて「更新」
    4. 「制限付きのスコープ」に上記 3 つが追加されているのを確認したら「保存して次へ」
  1. テストユーザー
    • 「+ ADD USERS」をクリックし、Google アカウントのメールアドレスを入力し「保存して次へ」
    • ユーザーを追加する必要はないので、そのまま「保存して次へ」
  2. 概要
    • 確認して「ダッシュボードへ戻る」
失敗例

当初は、公開ステータスを「テスト」、テストユーザーに「Google アカウントのメールアドレス」を設定して上手く行ったように見えたのですが、一週間後に繋がらなくなってしまいました。

ls GoogleDrive
(out) ls: cannot access 'GoogleDrive': Input/output error

ログを見ると ssh key が期限切れになっています。

google_guest_agent[407]: ERROR non_windows_accounts.go:218 invalid ssh key entry - expired key: ユーザー名:ssh-rsa (RSAキー) google-ssh {"userName":"Google アカウントのメールアドレス","expireOn":"2024-08-07T00:57:34+0000"}

公開ステータスが「テスト」だと一週間しか有効期限が無いため、永続的に使用するためにはアプリを公開して「本番環境」にしないとダメなようです。

OAuth 同意画面のダッシュボードから「アプリを公開」します。

公開ステータスが「本番環境」になっていれば OAuth 同意画面の設定は完了です。

確認ステータスに「確認が必要」となっていますが、一般に公開はしない(自分しか使わない)ので、このままで大丈夫です。

認証情報

続いて「認証情報」へ移り、OAuth 2.0 クライアント ID を作成します。

  1. 「認証情報」を開き、「+ 認証情報を作成」をクリックします。
  2. OAuth クライアント ID」をクリック
  3. OAuth クライアント ID の作成の画面で下記を入力して「作成」
    • アプリケーションの種類:ウェブ アプリケーション
    • 名前:(何でも良いので入力)
    • 承認済みのリダイレクト URI:http://127.0.0.1:8080/oauth2callback
  4. 「OAuth クライアントを作成しました」という画面になるので、開いたままにしておきます。
    ※閉じてしまった場合でも「Oauth クライアントをダウンロード」ボタンから再度表示できます。

google-drive-ocamlfuse と Google Drive API の連携設定

一旦 API とサービスから離れて、ブラウザでの SSH に戻ります。

作成した OAuth クライアントの「クライアント ID」と「クライアント シークレット」を使って、google-drive-ocamlfuse のヘッドレス認証を行います。

google-drive-ocamlfuse -headless -id <クライアント ID> -secret <クライアント シークレット>

すると、認証用の URL が表示されるので、アドレス(https://accounts.google.com 以降行末まで)をコピーしてブラウザで開きます

Please, open the following URL in a web browser: https://accounts.google.com/o/oauth2/auth?client_id=<クライアント ID>&redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Foauth2callback&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&response_type=code&access_type=offline&approval_prompt=force
Please enter the verification code:

ブラウザで Google アカウントにログインすると、「このアプリは Google で確認されていません」という画面になりますが「詳細」をクリックします。

OAuth 同意画面の「確認ステータス」で確認を受けていないため警告が表示されていますが、「アプリ名(安全ではないページ)に移動」をクリックします。

すると「アプリが Google アカウントへのアクセスを求めています」という画面になるので「続行」します。

次の画面で「サイトにアクセスできません」と表示されますが、これは正常な挙動です。

アドレスバーの「code=」と「&scope」に挟まれた部分の確認コードをコピーします。

http://127.0.0.1:8080/oauth2callback?code=(確認コード)&scope=https://www.googleapis.com/auth/drive

ブラウザでの SSH に戻り、確認コードを貼り付けます。

Please enter the verification code: (確認コード)
Access token retrieved correctly.

Access token retrieved correctly. と表示されたら無事完了です。

認証に失敗した場合

~/.gdfuse/default/ を削除して、再度認証してください。

マウントの確認

無事に連携はできたので、マウントしたドライブが見えるか確認しておきましょう。

まずはマウントするディレクトリを作成します。

mkdir ~/GoogleDrive
ls ~/GoogleDrive
(out) (空行)

Google Drive をマウントして、一覧を表示します。

google-drive-ocamlfuse ~/GoogleDrive
ls ~/GoogleDrive
(out) (Google Drive のマイドライブのファイル・フォルダ)

Google Drive をアンマウントします。

fusermount -u ~/GoogleDrive

公開フォルダを指定する

このままでは、Google Drive 内のすべてのファイルが見えてしまいます。

DriveToWeb では、Google Drive のルートディレクトリ(マイドライブ)の「public_html」フォルダを公開していましたので、同じように設定します。

Google Drive の公開したいフォルダ(public_html)を開きます。URL の最後のスラッシュ以降「https://drive.google.com/drive/folders/(フォルダ ID)」をコピーします。

google-drive-ocamlfuse の設定ファイルに、ルートフォルダを公開フォルダに設定し、読み取り専用にしておきます。

vi ~/.gdfuse/default/config
#read_only=false
read_only=true

#root_folder=
root_folder=(フォルダ ID)

最初に接続した時のキャッシュを削除してから、再び一覧を取得します。

rm ~/.gdfuse/default/cache/cache.db
google-drive-ocamlfuse ~/GoogleDrive
ls ~/GoogleDrive
(out) (公開フォルダのファイル一覧)
fusermount -u ~/GoogleDrive

FUSE のオプションを修正

デフォルトでは google-drive-ocamlfuse を実行したユーザーでないと、Google Drive をマウントしたディレクトリを参照することができません。

このままでは Nginx から見られませんので、他のユーザーからでも参照できるよう FUSE の設定ファイルを修正します。

sudo vi /etc/fuse.conf

「user_allow_other」を有効に(コメント解除)して保存します。

#user_allow_other
↓ (コメントを解除する)
user_allow_other

google-drive-ocamlfuse のサービス化

サーバー起動時に、自動的に Google Drive をマウントするために、google-drive-ocamlfuse をサービス化します。

/etc/systemd/system/ に google-drive-ocamlfuse.service を新規作成します。

sudo vi /etc/systemd/system/google-drive-ocamlfuse.service

google-drive-ocamlfuse のコマンドは絶対パスで指定します。また、起動オプションに「-o allow_other」を追加して、マウントしたドライブを他のユーザーから参照できるようにします。

[Unit]
Description=FUSE filesystem over Google Drive
After=network.target
 
[Service]
User=ユーザー名
Group=ユーザー名
ExecStart=/home/ユーザー名/.opam/default/bin/google-drive-ocamlfuse /home/ユーザー名/GoogleDrive -o allow_other
ExecStop=fusermount -u /home/ユーザー名/GoogleDrive
Restart=always
Type=forking
 
[Install]
WantedBy=multi-user.target

google-drive-ocamlfuse.service をオン・オフして、意図した通りの挙動になる事を確認します。

sudo systemctl start google-drive-ocamlfuse.service
ls ~/GoogleDrive
(out) (公開フォルダのファイル一覧)
(out) 
sudo systemctl stop google-drive-ocamlfuse.service
ls ~/GoogleDrive
(out) (空行)

サーバーの起動時に、サービスを自動起動します。

sudo systemctl enable google-drive-ocamlfuse.service
(out) Created symlink /etc/systemd/system/multi-user.target.wants/google-drive-ocamlfuse.service → /etc/systemd/system/google-drive-ocamlfuse.service.

これでサーバー起動時に、Google Drive の公開フォルダを自動的にマウントするようにできました。

ドメインの設定

続いて、サーバーにアクセスするためのドメインを発行してくれるサービスを選定します。

一昔前までは Freenom で無料ドメイン(.tk など)が取得できたようですが、現在は状況が変わってできなくなりました。

今回はサブドメインを割り当ててくれるサービスの DDNS Now をチョイスしました。

GCE はサーバーを起動する度にグローバル IP アドレスが変更されるので、DDNS サービスとは非常に相性が良いです。

DDNS Now - 無料ダイナミックDNSサービス
DDNS Nowは、永久無料のダイナミックDNSサービスです。短いドメインを匿名で簡単に取得できます。

登録を済ませたら、ユーザ名(サブドメイン名)と API 用トークンを確認しておきましょう。

この記事では GCE の「ユーザー名」表記と混同するため、DDNS Now で「ユーザ名」となっている部分を「サブドメイン名」と表記します。

DDNS の自動更新

GCE の外部 IP がエフェメラルの場合、サーバーを起動する度に IP アドレスが変わります。そのため DDNS に登録された IP アドレスも更新する必要があります。DDNS Now では API が用意されており、とても簡単に更新することができます。

マニュアル - DDNS Now

DDNS Now のマニュアルを参考に、更新用スクリプトを作成します。

vi ~/DDNSNowRenew.sh
#!/bin/bash
wget -O /var/log/DDNSNow_update.log "https://f5.si/update.php?domain=サブドメイン名&password=API用トークン"
chmod +x ~/DDNSNowRenew.sh

次に、更新用スクリプトをサービスとして登録します。

sudo vi /etc/systemd/system/ddnsnow-renew.service
[Unit]
Description=/home/ユーザー名/DDNSNowRenew.sh

[Service]
ExecStart=/home/ユーザー名/DDNSNowRenew.sh
Restart=no
Type=simple

[Install]
WantedBy=multi-user.target
sudo systemctl start ddnsnow-renew.service
sudo systemctl enable ddnsnow-renew.service

サーバーの起動時に、自動的にサービスを起動することで DDNS が更新されるようになりました。

SSL 証明書の取得

Certbot で DNS-01 チャレンジ認証を行うための API も用意されているので、サイトの SSL 化も簡単にできます。

特に、今回はファイアウォールで日本国外からの HTTP アクセスを弾いているので HTTP-01 チャレンジは失敗しますが、DNS-01 チャレンジに対応しているおかげで証明書を取得することができます。

マニュアル - DDNS Now

マニュアルの通りにコマンドを実行すれば、難なく証明書を取得することができました。

sudo apt install certbot
wget -O DDNSNowAcmeSetup.sh "https://ddns.kuku.lu/files/DDNSNowAcmeSetup.txt"
sh DDNSNowAcmeSetup.sh サブドメイン名 APIトークン
sudo certbot certonly --server https://acme-v02.api.letsencrypt.org/directory --manual --preferred-challenges dns --manual-auth-hook ~/.ddnsnow/DDNSNowAcmeAuthHook.sh -d サブドメイン名.f5.si --agree-tos -m メールアドレス

DDNS Now はユーザー目線の工夫が凝らされていて、とても素晴らしい!

Nginx でサイトを公開する

最後にサイトを公開するための Nginx をインストールしていきます。

sudo apt install nginx

Nginx の設定ファイルを、ドメイン名(サブドメイン名.f5.si)で作成します。

sudo vi /etc/nginx/sites-enabled/サブドメイン名.f5.si
server {
  listen 80;
  listen 443 ssl http2;
  server_name サブドメイン名.f5.si;

  root /home/ユーザー名/GoogleDrive;
  index index.html;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_certificate /etc/letsencrypt/live/サブドメイン名.f5.si/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/サブドメイン名.f5.si/privkey.pem;

  server_tokens off;
  add_header X-Frame-Options SAMEORIGIN;
  add_header X-Content-Type-Options nosniff;
  add_header X-XSS-Protection "1; mode=block";
  add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";
}

Nginx を起動します。

sudo systemctl start nginx.service
sudo systemctl enable nginx.service

SSL 証明書の自動更新

SSL 証明書は、certbot.timer によって自動的に更新されます。更新された際に、renewal-hook(deploy)で Nginx を再起動して反映するようにします。

sudo touch /etc/letsencrypt/renewal-hooks/deploy/nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx
sudo vi /etc/letsencrypt/renewal-hooks/deploy/nginx
#!/bin/bash
systemctl restart nginx.service

動作確認

以上で全ての設定が完了しました。動作を確認するために、一度サーバーシャットダウンします。

sudo shutdown -h now
(out) Broadcast message from root@xxxxxxxxxx on pts/1:
(out) The system will power off now!
  1. VM インスタンスを「開始 / 再開」して、外部 IP が変更されたことを確認します。
  2. DDNS Now にログインし、「Aレコード」が変更後の IPv4 アドレスになっているか確認します。
  3. ブラウザから https://サブドメイン名.f5.si を閲覧して Google Drive の内容が表示されるか確認します。

まとめ

転送量制限(1GB/月)やアクセス制限(中国とオーストラリアは NG)という制約がありますが、Google Drive のホスティングサーバーを無料で構築できました。

DriveToWeb と併用することもできるので、DriveToWeb が繋がらないときだけ今回のサーバーを見てもらうなど、バックアップ的な使い方もできます。

参考にしたサイト

【2025年最新版】SSH接続しかできないAWS内の仮想マシン(Debian)からGUI無しでGoogle Driveにアクセスする
SSH接続・CUI操作のLinux(Debian)にgoogle-drive-ocamlfuseをつかってGoogleDriveをマウントする - Qiita
今日は、SSH接続・CUI操作のLinuxにgoogle-drive-ocamlfuseを使ってGoogleDriveをマウントする方法を書きます。 なぜやろうと思ったのか 最近、GCPのSpot Virtual MachinesからGoo...

コメント

タイトルとURLをコピーしました