自宅のパソコンで立ち上げた仮想マシンを公開するまで
2025-03-13
概要
仮想マシン(VM)を立てて、外部に公開するまでにやったことを書き連ねる備忘録です。
はちゃめちゃに間違ったことを書いている可能性があります。もしご自身のPCで行う場合は入念な下調べをすることをおすすめします。
私がたどった流れを並べると以下のようになります。かなり遠回りしました。必要ない手順もありました。
- qemu-kvmでVMをたてる (VM特有)
- defaultでNATをつかう設定→bridgeでホストとVMを並列にする (VM特有)
- とりあえずVMにNginxを入れてみる。
- 外部公開を試みるも、環境が良くなくてうまく行かなそうだったので断念
- 外部に何らかのサーバを用意(VPNサーバーにするため)
- 外部サーバーをVPNサーバーに、VMをVPNクライアントして接続する
- 外部サーバーの80番ポートへのアクセスをVMの80番ポートに横流しする設定
1. qemu-kvmで仮想マシンを構築
※この章は、VMを立ち上げたい方に向けています。VMを使わず、ホストをそのまま公開する方などは飛ばしてください。
まずは仮想マシンを立ち上げましょう。
私のホストOSはUbuntuなので、そのままターミナルで操作をしていきます。
必要なものをインストール
sudo apt update
sudo apt upgrade
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients brdge-utils
sudo apt install virt-manager
インストールができたら、KVMを使えるように権限付与を行います。
sudo chmod a+rw /dev/kvm
次に、libvirtdを起動します。
sudo systemctl enable --now libvirtd
Ubuntuのisoを取得
今回はUbuntuのVMを構築します。お好きな方法でisoを取得してください。
Ubuntuを入手する | Ubuntu
Ubuntuは、スマートフォン、タブレット端末、PCからサーバー、クラウドまであらゆる環境で動作するオープンソースのソフトウェアプラットフォームです。
私はこのページからダウンロードしました。
VM構築
次に、VMを構築します。まずはvirt-manager起動
virt-manager
そしたら、左上の「新しい仮想マシンの作成」を押下。 先程ダウンロードしたisoを選択して、ぽちぽち進めます。
私はネットワーク設定はデフォルトのまま進めました。いずれはブリッジにしますが。
2. VMをブリッジ接続
※この章はVMをVPNなどを用いずに公開する場合には必須だと思いますが。VPNつかうなら正直必要ないです。
VMのネットワーク設定はデフォルトでNATです。
NATだと外部からの接続が難しくなってしまうので、なんとかしたいところです。
対策は主に以下の2つだと思われます。
- ブリッジ接続でVMとホストを同列に扱う
- ホスト側でポートフォワーディングする
私は前者を取りました。
まずはnetwork-managerを確認しましょう。
nmcli
もしインストールされていないようでしたら、以下のコマンドでインストールしてください。
sudo apt install network-manager
network-managerをインストールできたら、早速自分のPCのネットワークデバイスを眺めてみましょう。
sudo nmcli device show # nmcli d sでもOK
コネクションも眺めてみます。
sudo nmcli connection show
現在の自身のPCの状況を把握したら、早速ブリッジを作成していきましょう。
犬でも分かるLinuxネットワーク設定(3): 仮想ブリッジ
サーバ上で仮想マシンを利用していると、仮想マシンに物理ネットワークのIPアドレスを割り当てたくなることがあります。その場合は、ホストとなるサーバに仮想ブリッジを作成し、仮想ブリッジを経由して仮想マシンを物理ネットワークに接続させます。今回は

このサイトを参考にしたりしながらやりました。
先ほど紹介した一覧表示コマンドを駆使して、ブリッジを割り当てるIPアドレスを決めます。(以下では、そのIPアドレスが192.168.xx.xxであるとして書きます)
もともとそこにつながっていたものは、ブリッジとつなげて間接的にそこにつながっている状態にします。
手順をこなすごとにnmcli c sで今どういう状態になっているのか確認するのがおすすめです。
まずブリッジを作りました。
nmcli connection add type bridge con-name br0 ifname br0
br0はただのお名前なので、bridgeとかbr1とかでも大きな問題はないでしょう。
作り終わったら、状態を確認。
nmcli c s
できたら、今作ったbr0に設定を追記していきます。
なお、設定の状態は
nmcli device show br0
で見られます。
基本は先程あげたサイト通りです。 まずは最初に見定めたIPアドレスを設定
nmcli connection modify br0 ipv4.method manual
nmcli connection modify br0 ipv4.addresses 192.168.xx.xx
次に、ゲートウェイ設定をします。要は出入り口のことで、私の場合は自宅のルーターがそれに該当します。
ip route
で、defaultのIPアドレスをゲートウェイとして追加します。
nmcli connection modify br0 ipv4.gateway なんたらかんたら
DNSもルーターにします。
nmcli connection modify br0 ipv4.dns なんたらかんたら
次に、スパニングツリーを無効もともとそこにつながっていたものは、ブリッジとつなげて間接的にそこにつながっている状態にします。にします。これは送信データのループを防ぐためのものですが、記事によると単独ブリッジの場合には不要とのこと。
nmcli connection modify br0 bridge.stp no
ちなみにstpというのはスパニングツリーのプロトコルの一種です。
もしこのあたりでネットが繋がらなくなったら、スパニングツリーを有効化しましょう。
次は、
もともとそこにつながっていたものは、ブリッジとつなげて間接的にそこにつながっている状態にします。
これをやります。
nmcli connection add type ethernet con-name br-slave-[もともとそこにいたコネクション] ifname [もともそそこにいたコネクション] master br0
これによって、もともとそこにあった接続を、br0をマスターとするスレイブとして追加することができました。
では、もともとあった接続をoffにして、今作ったbridgeとslaveをonにしましょう。
nmcli connection down [もともとあったコネクション名]
nmcli connection up br-slave-[もともとあったコネクション名]
nmcli connection up br0
一連の操作が終わったら、ちゃんと接続がなされているか確認してみましょう。
nmcli c s
どうですか、できてますか。
成功していた方、おめでとうございます。
しかし、まだ終わりではありません。VMをこのブリッジに繋げなくてはならないのです。
でも、この手順はめちゃめちゃかんたん。
virt-managerのGUIをポチポチやればすぐですよ。
まず、仮想マシンを「開く」していただいて、その画面の「仮想マシン(M)」の詳細を表示。
NICをみて、仮想ネットワークインターフェースのネットワークソースをブリッジデバイスに、デバイス名を先程設定したブリッジに(今回ならbr0)。
これでVMのブリッジ接続が完了しました。
試しにVMを立ち上げて、VMのなかで
ip a
してみてください。 ホストOSとは別のIPアドレスがちゃんと振り分けられているのが確認できるかと思います。
ここまでできたら完璧!
VMがホストOSと同列になりました。
3. とりあえずVMにNginxを入れてみる
ここの章はほんとにいらんですね。 表示確認用に、80番ポートにアクセスが来たときにNginxのデフォルトページが表示されれば便利かなと思って入れました。
VMの中にNginxを入れます。
sudo apt install nginx
で、入れたらとりあえず状態を確認
sudo systemctl status nginx
良さそうなら有効化
sudo systemctl enable nginx
あとは、http://[VMに割り当てられているIPアドレス]にアクセス。Nginxのデフォルトページが見れるでしょう。
4. 直接の公開を諦めた話
ここまでの設定をしていれば、あとはIPアドレスを固定して公開すればよいのですが、それがどうもうまくいかなそう。
IPの固定はデバイス側で行う方法と、ルーター側で行う方法があります。
調べたところ、デバイス側で行う方法はルータの割当との競合を気にする必要がありそう。手軽に行えるルータ側で設定することに。
IP固定のあとも、公開手順をルーターの説明書を見ながら行っていたのですが、ルータに割り当てられてるIPを確認したところ、これがプライベートIPでした。
つまり、私の手の届く範囲でいじれる最上層のルータもより大きなNATによってIPを割り当てられていることになります。これの解決はちょっと面倒そう。
よって、直接開放ではない方法を試すことにしました。
それが、これから記すVPNを使った手法です。
5. 外部サーバーを用意
外部からアクセスできるサーバーを用意して、そこに来たアクセスをVPNを通してNATの中のVMに飛ばします。
そのためにグローバルIPアドレスでアクセスできる外部サーバーを用意しましょう。
お好きな方法で構いません。私はAWSで用意しました。
Ubuntuサーバーです。
サーバー側でセキュリティ設定ができる場合は、
- sshで使うための22番ポート/TCP
- nginxを入れてみたので、表示できるように80番ポート/TCP
- VPN通信のために使う51820番ポート/UDP このあたりのポートは開けておいたほうがいいと思います。
6. 外部サーバーとVMをVPNでつなげる
外部サーバーをVPNサーバーとして、VMをVPNクライアントに指定。
この2つの通信を確立するところから始めましょう。
VPNの種類もいくつかありますが、今回はセットアップがカンタンなWireGuardを使って見ます。
以下のサイトなどを参考にセットアップをしていきます。
第614回 WireGuardでVPNサーバーを構築する | gihyo.jp
今回は、最近Linuxカーネルにマージされたことでも話題となっているWireGuardを用いて、VPNサーバーを構築する方法を紹介します。
このサイトを参考にしたりしながらやりました。
基本方針は以下の通り
- クライアント・サーバー両方で秘密鍵、公開鍵を設定
- 鍵を交換して、設定ファイルに組み込む+IPを割り当てる
- WireGuard起動! この3つだけです。とてもカンタンですね。
公式ページを見ると、WireGuardは、ファストでシンプルなことを目指して開発されていそう。最高。
WireGuard: fast, modern, secure VPN tunnel
早速やってみましょう。
1. クライアント・サーバーの両方で鍵を作成する。
以下に書く手順はクライアント、サーバーの両方で行う手順です。 まず、パッケージのインストールを行います。
sudo apt install wireguard
次に、鍵を作成します。名前は、クライアント、サーバーどちらのものかわかりやすいようにします。
wg genkey | sudo tee /etc/wireguard/server.key
sudo chmod 600 /etc/wireguard/server.key
秘密鍵をもとに、公開鍵を作りましょう。
sudo cat /etc/wireguard/server.key | wg pubkey | sudo tee /etc/wireguard/server.pub
sudo chmod 600 /etc/wireguard/server.pub
上記の手順をクライアント側でも行います。
wg keygen | sudo tee /etc/wireguard/client.key
sudo chmod 600 /etc/wireguard/client.key
sudo cat /etc/wireguard/client.key | wg pubkey | sudo tee /etc/wireguard/client.pub
sudo chmod 600 /etc/wireguard/client.pub
2. 鍵を交換して、設定ファイルに書き込む+IP割り当て
これはクライアント・サーバーどちらにも必要です。
今回は、WireGuardのVPNに10.0.0.0/24というサブネットを割り当てます。
サーバー側のIPを10.0.0.1、クライアント側のIPを10.0.0.2とします。
また、作成するインターフェース名をwg0とします。
設定ファイルの名前はこのインターフェース名を使わなくてはなりません。
お好きな方法で/etc/wireguardの中にwg0.confというファイルを作成します。(クライアント、サーバーともに)
ひとまず、サーバー側の設定を済ませてしまいましょう。
[Interface]
PrivateKey = あなたの作成したサーバー秘密鍵
Address = サーバーに割り当てるIPアドレス(今回なら10.0.0.1)
ListenPort = 51820
[Peer]
PublicKey = あなたの作成したクライアントの公開鍵
AllowedIPs = 10.0.0.2/32(クライアントにあなたが割り当てたIP)
次に、クライアント側の設定をしましょう。
[Interface]
PrivateKey = あなたの作成したクライアント秘密鍵
Address = クライアントに割り当てるIPアドレス(今回なら10.0.0.2)
[Peer]
PublicKey = あなたが作成したサーバー公開鍵
EndPoint = サーバーのIPアドレス:51820
AllowedIPs = 10.0.0.1/24
3. WireGuard起動!
WireGuardは、ラッパースクリプトを使ってカンタンに起動することができます。
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
起動が完了したら、サーバーはリッスン状態に入ります。
次に、クライアントからリッスン状態にあるサーバーと接続していきましょう。
sudo wg-quick up wg0
接続状態は
sudo wg show
で確認できます。
7. 外部サーバーにきたアクセスをVMに流す設定
ファイヤーウォールを設定をいじって外部サーバーに来たアクセスを横流ししていきます。
iptablesを使ってファイアウォールをいじっていきます。
まずはパケットのフォワードを許すように設定を変更しましょう。
/etc/sysctl.confをお好きなエディターで開いて編集します。
net.ipv4.ip_forward=1
デフォルトでこの設定はコメントアウトされているので、コメントアウトを外して保存するだけでOK。
sysctlコマンドで設定反映させましょうか。
sudo sysctl -p
サーバー側でDNAT、SNATの設定をしていきます。
設定するルールの要件をまとめておきます。
- NATテーブルに
- PREROUTINGとして
- 受信インターフェースはこのVPNサーバーに限って
- TCPプロトコル(今回は)
- 80番ポートに来た通信を
- DNAT実行して
- 10.0.0.2の80番(VPNクライアント)に宛先変更
これを実行するiptablesルールを追加しましょう。
enX0は実際の自分のデバイス似直してください。
ip aとかnmcliとかで確認してください。
sudo iptables -t nat -A PREROUTING -i enX0 -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:80
一応SNATを設定しておきます。
sudo iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
これで、wg0からの返答をVPNサーバーのグローバルIPからの返答として変換できます。
これで、外部からVPNサーバーにアクセスすると、ローカルにあるVMのNginxのデフォルトページが見れるはずです。
まとめ
私が手探りでやったことを思い出しながら書いたので、誤っている部分も多々あるかと思います。
再三にはなりますが、ご自身でしっかり情報収集してから行うことをおすすめします。