まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

2020/10

サブジェクトが少しわかりにくいと思ったので最初にやりたいことを補足しておきます。

ウェブサービスを公開する際に Basic 認証と呼ばれる認証機能を有効にすることがあります。アクセス時にユーザーIDとパスワードが聞かれ、正しい組み合わせを入力しないと先に進めなくなる、というものです。会員制サービスや、正式公開前のサービスを限られた人だけで使いたい場合、グーグル等の検索エンジンクローラーに見つからない状態で運用したい場合などによく使われます:
thumb_basic


今回やりたかったのは、この Basic 認証を例えば以下の条件で実現するような Node.js アプリケーションを作ることです:
・パス /hello 以下にアクセスした際に Basic 認証が必要
・/hello にアクセスするには URL パラメータ id が必要(つまり GET /hello だけではエラーとなり、GET /hello?id=XXX というフォーマットでアクセスする必要がある)
/hello?id=XXX の時と /hello?id=YYY の時とでは Basic 認証のユーザーIDやパスワードが異なる


最後のが今回の肝となる条件です。パラメータ id の値ごとに Basic 認証のユーザーIDやパスワードが変わり(データベース等に格納されているものを id をキーに取り出して比較するイメージ)、これを Node.js + Express 環境でどのように実現するか、というのが挑戦内容です。


やりたいことが明確になったところで、改めて Node.js + Express 環境で Basic 認証をかける方法をググってみると、basic-auth-connect モジュールを使う方法がメジャーな方法の1つとして見つかります。これは簡単にいうと以下のような感じで Basic 認証をかけるものことができるものです:
var express = require( 'express' ),
    basicAuth = require( 'basic-auth-connect' ),
    app = express();

app.all( '/hello*', basicAuth( function( user, pass ){
  return ( 'username' === user && 'password' === pass );
}));

  :
  :

app.get( '/hello', function( req, res ){
  res.contentType( 'application/json; charset=utf-8' );
  res.write( JSON.stringify( { status: true }, 2, null ) );
  res.end();
});

  :
  :

GET /hello リクエストに対しては単に { status: true } という JSON を返すだけの定義がされていますが、その前に Basic 認証を有効にする部分が記述されています。この例では(/hello に何らかの URL パラメータが付属する場合も含めた) /hello* というパスに GET リクエストが行われた場合に Basic 認証が必要になり、ユーザーID 'username' 、パスワード 'password' が入力された場合のみ true(認証成功)で実際の GET /hello の処理が行われ、それ以外の場合は false(認証失敗)という扱いとなって再度入力が求められたり、何度か間違えると認証エラー扱いとなる、というものです。とても便利で、よく使っています。


さて、今回は上述の条件で Basic 認証を有効にする必要があり、少し異なる処理が必要です。正しいユーザーIDとパスワードは URL パラメータ id によって変わるのですが、この URL パラメータは req オブジェクから取り出す必要があり、今の形のままでは(認証判断時に req オブジェクトが取得できないので)取得が難しそうです。自分もこの basic-auth-connect モジュールを使う前提で実装を考えていたので詰まってしまいました。。

結論としては basic-auth-connect モジュールを使うことを諦め、自分で認証判断してエラー時にエラーコードを返す、という地味な処理に切り替えて実装できました:
var express = require( 'express' ),
    //basicAuth = require( 'basic-auth-connect' ),  //. basic-auth-connect は使わない
    app = express();

//. パラメータ id 毎に必要なユーザーIDとパスワード(本当はデータベース等から取得するイメージ)
var db = {
  "000" : { user: 'a', pass: 'x' },
  "001" : { user: 'b', pass: 'y' },
  "002" : { user: 'c', pass: 'z' }
};

/* URL パラメータ毎に認証情報を変えたい */
app.use( function( req, res, next ){
  //. hello* へのリクエスト時かどうかを判断
  var originalUrl = req.originalUrl;
  if( originalUrl.startsWith( '/hello' ) ){
    //. URL パラメータ ID を取り出す
    var id = req.query.id;
    //. 指定された ID のユーザー ID とパスワードが存在しているかどうかを調べる
    if( db[id] ){
      //. ヘッダから入力されたユーザーIDとパスワードを取り出す
      var b64auth = ( req.headers.authorization || '' ).split( ' ' )[1] || '';
      var [ user, pass ] = Buffer.from( b64auth, 'base64' ).toString().split( ':' );

      //. 入力内容が正しい場合のみ next() を返して本来の処理へ
      if( db[id].user == user && db[id].pass == pass ){
        return next();
      }else{
        //. 入力内容が間違っていた場合は認証エラー扱いとする
        res.set( 'WWW-Authenticate', 'Basic realm="MyApp"' );
        res.status(401).send( 'Authentication required.' );
      }
    }else{
      //. 指定された ID が存在していなかった場合も認証エラー扱いとする
      res.set( 'WWW-Authenticate', 'Basic realm="MyApp"' );
      res.status(401).send( 'Authentication required.' );
    }
  }else{
    return next();
  }
});

  :
  :

app.get( '/hello', function( req, res ){
  res.contentType( 'application/json; charset=utf-8' );
  res.write( JSON.stringify( { status: true }, 2, null ) );
  res.end();
});

  :
  :


赤字部分が今回作成した処理です。req オブジェクトからリクエスト先のパスや Basic 認証で指定された情報を取り出して正しい情報かどうかを判断し、正しい場合は本来の処理へ、間違っていた場合は HTTP の認証エラー結果を返すような内容を記述しています。basic-auth-connect モジュールを使うとこのあたりの細かな記述をする必要がなかったのですが、自分で判断する場合はこのあたりも自分の責任範囲で用意する必要があります。

上述の例では URL パラメータ id は "000", "001", "002" のいずれかである必要があり、それぞれの場合の Basic 認証情報(ユーザーID : パスワード)はそれぞれ "a":"x", "b":"y", "c":"z" としています。この正しい組み合わせが指定された場合のみ GET /hello が実行されて結果が返される、という処理が実行されるようになります。


(参照)
https://stackoverflow.com/questions/23616371/basic-http-authentication-with-node-and-express-4


ウェブ画面でタイル状のブロックを縦方向にいくつか並べて表示する際に「1つ上のタイルにぶらさがる」感じを出したいことがままあります。

例えば並べるブロックの数が不定で、画面を一度表示した後からブロックを追加したくなったりする際に「1つ前のブロックの続きである」ことを明確にするため、1つ前のブロックにぶら下がる感じで表現したい、というケースです。更に具体的にはブロックチェーンのブロックを並べるようなケースなどで、こんな風に:
2020101300


この「ぶらさがってる」感じはブロックの左右に紐状(上図ではチェーン)の画像を表示することで目的に近いものが表現できます。これを CSS で実現するにはどうすればよいか、というのが今回のテーマです。なお今回はブロック部分を Bootstrap4 の card クラスを使って実現していますが、他の多くの環境でも同様に実現できる、はず。

まず紐となる画像を用意しました。今回用意したのは↓のチェーン状の画像で、サイズは 23x50 ピクセルです(画像の高さである 50 を前提に以下の CSS を記述します):
chain


また HTML 的には以下のようにしたいと思っています。今回は Bootstrap4 の card クラスが付与された <div> を複数並べるのですが、2つ目以降の <div> には mycard クラスも付与し、この mycard クラスの CSS で2つ目以降のブロックは1つ前のブロックにぶら下がっているように表現できることを目指します:
<div class="container" style="padding:80px 0;">
  <div class="card text-white bg-primary border-danger">
    <div class="card-body">
      <h4 class="card-title">Title #0</h4>
      <p class="card-text">Text for #0</p>
    </div>
  </div>

  <div class="card text-white bg-primary border-danger mycard">
    <div class="card-body">
      <h4 class="card-title">Title #1</h4>
      <p class="card-text">Text for #1</p>
    </div>
  </div>

  <div class="card text-white bg-primary border-danger mycard">
    <div class="card-body">
      <h4 class="card-title">Title #2</h4>
      <p class="card-text">Text for #2</p>
    </div>
  </div>


    :
</div>

で、この mycard クラスをどのように定義すればよいか? が今回の課題なのですが、こんな感じで実現してみました:
<style type="text/css">
.mycard{
  margin: 55px 0;
}
.mycard::before{
  content: url(./chain.png);
  margin: 0px;
  position: absolute;
  top: -52px;
  left: 10px;
}
.mycard::after{
  content: url(./chain.png);
  margin: 0px;
  position: absolute;
  top: -52px;
  right: 10px;
}
</style>

まず mycard クラスを付与するブロックは画像(./chain.png)の高さぶんだけの余幅を持って表示される必要があります(その余幅に画像が入ります)。今回、画像の高さが 50px なので、少し余裕をもたせて margin: 55px 0; というスタイルを指定しています(つまり実際には 55px ぶん下に表示されることになります)。

この空いた余幅に画像を含めたいのですが、今回は上述のように左端と右端の両方に画像を入れ、2箇所で引っかかっているようにぶら下げようとしています。そのため ::before 要素::after 要素両方を使って mycard クラスの左端と右端に画像を差し込みます。

まず ::before 要素ですが、content に目的の画像を含めます(content: url( './chain.png' );)。更に画像位置をブロックの左端に設定するため、position: absolute; top: -52px; left:10px; を指定しています。これによって 50px の高さをもつ画像を 52px ぶん上から表示させて空間の隙間に収まるようにしています(また左端ギリギリではなく、10px ぶんのマージンを取っています)。

::after 要素にも同様の設定を行いますが、こちらは右端に表示したいため right: 10px; と、ブロックの右側に 10px ぶんのマージンができるような位置を指定しています。これによってブロックの右側に画像を表示させるよう指定しています。

こうして出来上がったサンプルがこちらです:
https://dotnsf.github.io/hanging_css/

2020101301


一番上の #0 には mycard クラスがついていないので、パネルのブロックがそのまま表示されてます。#1 以下には全て mycard クラスを指定しているのでチェーンでぶらさがる効果が付与されています。

この方法ならいくつでも足せるし、JavaScript で後から動的に足すこともできるし、ブロックの要素に mycard クラスを1つ追加するだけで実現できるので簡単です。

上記ページのソースコードをまるごと参照していただき、chain.png を同じフォルダに置いてブラウザで開けばローカルでも見れると思います。


贅沢をいうと、現在はチェーンの画像サイズに合わせて CSS を書いて調整しているのですが、この部分をもう少しスマートにできたらいいなあ、と。


Bootstrap を使うと、簡単にタブを実装することができます:

タブA

タブB

タブC




この中の特定のタブの選択したり、有効化/無効化を JavaScript(jQuery) で切り替える方法を調べ、シンプルな実装方法を見つけたので共有します。また上のタブとボタンで実際の挙動を確認できるようにしています。

まず、特定のタブを選択するにはタブのアンカー(<a id="XXX">)をセレクトして、
$('.tav-tabs a[href="#XXX"]').show( 'tab' );
というメソッドを実行することで選択できます。

またタブのアンカーにつける data-toggle="tab" という属性を取り除くことでタブとしての挙動を無効化することができます。逆に data-toggle="tab" という属性を付けてあげることで、無効化されていたタブを再び有効化することができるようになります:
//. 無効化
$('.tav-tabs a[href="#XXX"]').removeAttr( 'data-toggle' );

//. 有効化
$('.tav-tabs a[href="#XXX"]').attr( 'data-toggle', 'tab' );

あとはこれらを組み合わせることで特定のタブを選択したり、その際に他のタブを有効化/無効化したりすることで実現できるようになります。

上述の画面では以下のようなコードを実装しています。サンプルとしてどうぞ:
(HTML)
<div  class="container">
  <ul  class="nav nav-tabs">
    <li  class="nav-item"><a  href="#tab-a" data-toggle="tab" class="nav-link active">A</a></li>
    <li  class="nav-item"><a  href="#tab-b" data-toggle="tab" class="nav-link">B</a></li>
    <li  class="nav-item"><a  href="#tab-c" data-toggle="tab" class="nav-link">C</a></li>
  </ul>
  <div  class="tab-content">
    <div  id="tab-a" class="tab-pane active">
      <p  class="text-left">タブA</p>
    </div>
    <div  id="tab-b" class="tab-pane">
      <p  class="text-center">タブB</p>
    </div>
    <div  id="tab-c" class="tab-pane">
      <p  class="text-right">タブC</p>
    </div>
  </div>
</div>

<div  class="container">
<button  onclick="selectX();" class="btn btn-xs btn-primary" id="btnX">タブAを選択&タブAのみ有効</button>
<button  onclick="selectY();" class="btn btn-xs btn-success" id="btnY">タブBを選択&タブBとCのみ有効</button>
<button  onclick="selectZ();" class="btn btn-xs btn-warning" id="btnZ">タブCを選択&タブA, B, Cすべて有効</button>
</div>
(JavaScript)
functon activeTab( tabid ){
  $('.nav-tabs a[href="#' + tabid + '"]').tab( 'show' );
}
function enableTab( tabid ){
  $('.nav-tabs a[href="#' + tabid + '"]').attr( 'data-toggle', 'tab' );
}
function disableTab( tabid ){
  $('.nav-tabs a[href="#' + tabid + '"]').removeAttr( 'data-toggle' );
}

function selectX(){
  //. A を選択して A のみを有効にする
  activeTab( 'tab-a' );
  enableTab( 'tab-a' );
  disableTab( 'tab-b' );
  disableTab( 'tab-c' );
}
function selectY(){
  //. B を選択して B, C のみを有効にする
  activeTab( 'tab-b' );
  disableTab( 'tab-a' );
  enableTab( 'tab-b' );
  enableTab( 'tab-c' );
}
function selectZ(){
  //. C を選択して A, B, C すべてを有効にする
  activeTab( 'tab-c' );
  enableTab( 'tab-a' );
  enableTab( 'tab-b' );
  enableTab( 'tab-c' );
}



IBM Cloud のプライベートクラウド環境内にアクセスするための(サーバーインスタンス代以外は不要という意味で)安価な VPN サーバーを構築する、という機会があったので、その手順をまとめておきます。先に書いておくと、構築する際に手を動かす部分だけはさほど大変ではないのですが、事前準備というか、環境確認というか、構築用途を考慮した上で準備する部分がそこそこのボリュームを占めています。



【システム図】
今回紹介するシステム環境はこのようなものです:
2020100201

IBM Cloud 内にプライベート VLAN を1つ用意して、プライベートクラウド環境内に業務環境を構築します。このプライベートクラウドにアクセスするための VPN サーバー(上図の赤箱)を専用のサーバーインスタンスを1つ追加して構築する、というものです。

今回、このサーバーを CentOS 8.x をベースに、OpenVPN サーバーを使って構築します。OSS をベースとすることで、OS もソフトもサービスも追加料金不要です。


この作業を行う前に、環境についていくつか事前に決めておく必要があります。順を追って紹介します。


【セキュリティグループの定義】

セキュリティグループは IBM Cloud 内のセキュリティ設定を担う機能です。今回構築する VPN サーバーではどのようなセキュリティ設定で構築する必要があるかを事前に考え、定義し、この後作成する VPN サーバーに適用します。

※今回の記事の中で IBM Cloud に特化している部分でもあります。このセキュリティグループや内部ネットワークの確認方法に関わる箇所以外の内容は他のインフラ環境でもほぼ流用できると思っています。

IBM Cloud のセキュリティグループ定義内容を確認するには、まず左上のメニューから Classic Infrastructure を選択してインフラ一覧ページに移動します:
2020100202


そして同ページから更にメニューを Security - Network Security - Security Groups とたどった先にあります:
2020100203


デフォルトで定義済みのものも含めて、セキュリティグループの一覧が表示されます。新規セキュリティグループを追加する場合は画面右上の Create group を選択します:
2020100204


新規作成時のダイアログです。ここではセキュリティグループの Name(必須)と、必要に応じて Description(説明、任意)を入力します。なお Create group with .. と書かれたチェックは外して、空の定義状態で作成します:
2020100205


作成後の一覧です。入力した名前のセキュリティグループが登録されていることを確認します:
2020100206


一覧から1つ選択すると定義内容が表示されます。作成直後は定義内容が空の状態です。ここで Inbound(リクエスト受付)/Outbound(リクエスト送付) にわけてセキュリティ内容を定義します:
2020100207


例えば HTTP(=TCP/80) を通す設定を加えたい場合、Inbound ルールを以下の内容で追加します。特定のネットワークアドレスからのリクエストのみ受け付ける条件を加えることも可能です(無指定の場合はすべてのネットワークアドレスからのリクエストに適用されます):
2020100208


この作業を繰り返して、Inbound ルールと Outbound ルールを加えてゆきます。なお実際のサーバーインスタンスにはここで作成したルールを複数組み合わせて割り当てることができるので、1つのセキュリティグループにすべての設定内容を記述する必要はありません:
2020100201


更に、別のセキュリティグループで SSH を通すセキュリティ設定を用意しました。こちらは Source を定義することで、特定の IP アドレス(自分の IP アドレス)からのリクエストのみ許可するような内容にしており、環境構築後のこちらのセキュリティグループのみ除去する予定です:
2020100202


一覧ではこのように2つのセキュリティグループを追加しました:
2020100203


【プライベート VLAN ネットワーク環境の確認】

次に、この後 VPN サーバーをインストール&設定する際の、設定内容を現在のプライベート VLAN 内容から確認します。

まず対象サーバーインスタンスを1つ確認します。IBM Cloud ダッシュボードから Classic Infrastructure - Device List を選択します:
2020100204


(止まっているものも含めた)定義済みのサーバー一覧が表示されます。ここから今回 VPN で接続することになるプライベート VLAN 内のサーバーを1つ選択します:
2020100205


選択したサーバーインスタンスの詳細画面が表示されます。ここで以下の内容を確認しておきます(後の作業で使います):
2020100206


確認する項目この図での値
LocationTokyo 5
Private ネットワークの VLANtok05.bcr01a.2256
Private ネットワークの IP Address10.193.122.107/26


この IP アドレスの値より、同ネットワークのネットワークアドレスおよびネットマスクを計算すると、
 ネットワークアドレス : 10.193.122.64
 ネットマスク : 255.255.255.192
ということがわかります。


サーバー構築前に調べておく内容は以上です。この後は実際のサーバー構築に移ります。


【CentOS 8 インスタンス作成】

では実際に VPN サーバーを構築していきます。まずは IBM Cloud の同プライベート VLAN 内に CentOS 8.x の VM を追加します。

IBM Cloud ダッシュボードのデバイス一覧画面から Order ボタンを選択します:
2020100207


追加内容はいわゆる VM です。IBM Cloud では Virtual Server for Classic と分類されているので、これを直接検索するか、または検索窓に "classic" などと入力して検索し、Virtual Server for Classic を選択します:
2020100208


次の画面で作成する VM のスペックを決めていきます。名前に加えて CPU やらメモリやら。ここで注意すべきは作成するインスタンスの Location 、先程調べた VPN 接続対象プライベート VLAN(上記例では Tokyo 5)と同じものを選択してください:
2020100301


また OS イメージは CentOS 8.x を、そしてパブリックネットワークのセキュリティグループには上で作ったセキュリティグループ2つを指定して、自端末からの SSH 接続ができるようにします。最後に画面右の Create を選択します:
2020100302


指定した CentOS サーバーがデプロイされて稼働状態になるまで少し待ちます。

この後、この稼働したインスタンスにアクセスして作業するため、その接続のためのネットワーク情報を確認する必要があります。作成したインスタンスを選択します:
2020100304


まず選択直後は Overview ペインが選択され、概要が表示されています。この画面でパブリックネットワークに割り振られた IP アドレスを確認します(この直後の SSH ログイン時に指定します):
2020100305


また Passwords ペインを選択し、ログイン時のユーザー ID およびパスワードを確認しておきます。パスワードはマスクされていますが、目のアイコンを選択するとマスクが外れた状態で視認できるようになります:
2020100306


【CentOS 8 インスタンスへの OpenVPN サーバーのインストール&設定】

では作成した CentOS 8 インスタンスに SSH でログインし、OpenVPN サーバーとしてのセットアップを行っていきます。

まずは Teraterm や PuTTy、WSL などの SSH クライアントを使って、作成した CentOS 8 サーバーにログインします。先程確認した IP アドレス、ユーザー ID 、パスワードを使います:
2020100307


この後はしばらくこの SSH 端末からの入力作業が続きます。

まず最初に、この後 OpenVPN サーバーを EPEL リポジトリを使ってインストールすることになるため、EPEL リポジトリをインストールしておきます:
# dnf install -y epel-release

続けて OpenVPN サーバーや証明書作成に必要なツールなどをまとめてイントールします:
# dnf --enablerepo=epel -y install openvpn easy-rsa net-tools

ここまでの作業で OpenVPN サーバーそのものがインストールできました。続けて証明書ファイルを作成してゆきます(青字はコメントです):
(フォルダを移動)
# cd /usr/share/easy-rsa/3

(easyrsa 初期化)
# ./easyrsa init-pki

(認証局を作成)
# ./easyrsa build-ca

 この時にパスフレーズの入力を求められるので適当なフレーズを2回続けて入力。
 ここで入力したフレーズは下で再度入力する必要があります。


(サーバー証明書を作成) # ./easyrsa build-server-full server1 nopass
 途中でパスフレーズの入力を求められたら、上記で指定したフレーズを入力

(クライアント証明書を作成) # ./easyrsa build-client-full client1 nopass
 途中でパスフレーズの入力を求められたら、上記で指定したフレーズを入力

(DH を生成) # ./easyrsa gen-dh (TLS-Auth キーを生成) # openvpn --genkey --secret ./pki/ta.key (ここまでの作業で作成したファイル群をまとめて /etc/openvpn/server/ 以下にコピー) # cp -pR /usr/share/easy-rsa/3/pki/{issued,private,ca.crt,dh.pem,ta.key} /etc/openvpn/server/ (IP フォワードの設定のため、このファイルを新規作成) # vi /etc/sysctl.d/10-ipv4_forward.conf  (ファイル内容はこの1行だけ)  net.ipv4.ip_forward = 1 (IP フォワード有効化) # sysctl --system (サーバー設定ファイルのサンプルをコピー) # cp /usr/share/doc/openvpn/sample/sample-config-files/server.conf /etc/openvpn/server/ (サーバー設定ファイルを実際の環境に合わせて書き換え、変更内容は以下) # vi /etc/openvpn/server/server.conf  (78行目 コピーした証明書ファイル名を指定)  ca ca.crt  cert issued/server1.crt  key private/server1.key  (85行目 コピーした DH ファイル名を指定)  dh dh.pem    (101行目 VPN で使用する IP レンジを指定、ユーザーの環境と重複しないもの)  server 192.168.123.0 255.255.255.0  (142行目 コメントを解除して、上で確認したプライベート VLAN のネットワークを指定)  push "route 10.193.122.64 255.255.255.192"  (222行目 コメントを解除)  duplicate-cn  (267行目 コメントを解除して値を 32 (クライアント上限数)に変更)  max-clients 32  (287行目 ログファイルの保管場所をフルパス指定)  status /var/log/openvpn-status.log  (296行目 コメントを解除してログファイルの保管場所をフルパス指定)  log /var/log/openvpn.log  log-append /var/log/openvpn.log (変更内容を有効にして再起動) # systemctl enable --now openvpn-server@server
(プライベート VLAN とパブリック LAN のルーティング設定)
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT # iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT


※最後のルーティング設定部分はこのコマンドで実行した場合は再起動時に無効になってしまいます。恒久的に有効にする場合は /etc/rc.d.rc.local などに書いておく、などの対応が必要です。


これで対象プライベート VLAN に接続するための OpenVPN サーバーができあがりました。SSH はログアウトして構いません。


【OpenVPN クライアントのインストール&設定】

VPN サーバー側が用意できたので、残すは VPN クライアント側だけです。以下は Windows 10 を想定した内容で紹介します。

まだ OpenVPN クライアントをインストールしていない場合は以下からダウンロードしてインストールします:
https://openvpn.net/community-downloads/


導入先フォルダ(デフォルトだと C:\Program Files\OpenVPN)内の config サブフォルダ内に、VPN サーバーから以下の4ファイルを転送します(書き込み時に Administrator 権限が必要です):
  • /etc/openvpn/server/ca.crt
  • /etc/openvpn/server/ta.key
  • /etc/openvpn/server/issued/client1.ca
  • /etc/openvpn/server/private/client1.key

更に、導入先フォルダ内の sample-config サブフォルダ内にある client.opvn ファイルを、config サブフォルダ内にコピーします(書き込み時に Administrator 権限が必要です)。上記と合わせて都合5つのファイルを config フォルダにコピーすることになります。

そして、この client.opvn ファイルをテキストエディタで開き、以下を編集して保存します(保存時に Administrator 権限が必要です):
(42行目 OpenVPN サーバーのパブリック IP アドレスを指定)
remote 165.192.xxx.xxx

(88行目 コピーした各種証明書ファイル名を指定)
ca ca.crt
cert client.crt
key client.key

これでサーバーとクライアント、全ての準備が整いました。

なお、このクライアント向け作業で用意した5つのファイルは(後述の動作確認後に)他のユーザーからも利用できるようになります。サーバーからのファイル転送や client.opvn ファイルの編集といった面倒な作業をユーザー全員にさせる必要はなく、ここで動作が確認できていれば同じファイルを流用することができます。


【動作確認】

実際に OpenVPN サーバーにアクセスして VPN 接続します。Windows の場合であればタスクバーから OpenVPN クライアントを右クリックして client に接続します(アイコンが緑になったら VPN 接続成功です):
2020100308


VPN 接続ができたら、プライベートアドレスを指定してプライベート VLAN 内のサーバーにアクセスできるようになっているはずです。ウェブブラウザやリモートデスクトップ接続といったサービスをプライベートアクセスで使ってみてください:
2020100309

2020100309



このページのトップヘ