Grow up

生活とプログラミング

Ubuntu 18.04 自己署名証明書でSSLに対応する

はじめに

SSLに対応したテスト環境が必要になったので自己署名証明書を利用しました。
今回は証明書を発行して Google ChromeSSLに対応する方法をまとめます。
内容は example.com にアクセスして Nginx のトップページを表示します。

自己認証局の準備

証明書の保存場所を指定します。

$sudo vim /etc/ssl/openssl.cnf

[ CA_default ]

dir             = /etc/ssl/example-ca           # Where everything is kept

証明書の保存場所を作成します。

$mkdir /etc/ssl/example-ca

$cd /etc/ssl/example-ca

$ sudo mkdir certs
$ sudo mkdir private
$ sudo mkdir crl
$ sudo mkdir newcerts

$sudo sh -c "echo '01' > /etc/ssl/example-ca/serial" 
$sudo touch /etc/ssl/example-ca/index.txt
CA証明書の作成
$cd /etc/ssl/example-ca
#有効期限が10年の自己署名証明書を作成
sudo openssl req -new -x509 -newkey rsa:2048 -out cacert.pem -keyout private/cakey.pem -days 36500
#認証局のパスワードを入力
Enter PEM pass phrase:
#パスワードの再入力
Verifying - Enter PEM pass phrase:
#証明書の情報を入力
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Ota
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example CA
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:Example CA
Email Address []:email@example.com

2つのファイルが作成されている事を確認します。

#CA証明書
/etc/ssl/example-ca/cacert.pem
#CA証明書の秘密鍵
/etc/ssl/example-ca/private/cakey.pem
作成したCA証明書をブラウザにインポート

以下のコマンドで作成した example-ca.der をクライアント側で実行します。

$sudo openssl x509 -inform PEM -outform DER -in /etc/ssl/example-ca/cacert.pem -out /etc/ssl/example-ca/example-ca.der

Windows10 の場合になります。
exemple-ca.der をダブルクリックして起動して、[証明書のインストール]ボタンを左クリック。
f:id:knkomko:20210503011911p:plain:w350

"ローカルコンピュータ"を選択して[次へ]ボタンを左クリック。
f:id:knkomko:20210503012150p:plain:w350

"証明書をすべて次のストアに配置する"を選択して[参照]ボタンを左クリック。
f:id:knkomko:20210503012259p:plain:w350

"信頼されたルート証明機関"を選択して[OK]ボタンを左クリック。
f:id:knkomko:20210503012346p:plain:w250

[次へ]ボタンを左クリック。
f:id:knkomko:20210503012444p:plain:w350

[完了]ボタンを左クリック。
f:id:knkomko:20210503012518p:plain:w350

メッセージが表示されたらインストールは完了です。
f:id:knkomko:20210503012538p:plain:w250

自己署名証明書の作成
$sudo mkdir /etc/ssl/example.com/

$sudo openssl req -new -keyout /etc/ssl/example.com/example-key.pem -out /etc/ssl/example.com/example-csr.pem

#秘密鍵のパスワードを入力
Enter PEM pass phrase:
#パスワードの再入力
Verifying - Enter PEM pass phrase:
#証明書の情報を入力
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Ota
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example CA
Organizational Unit Name (eg, section) []:
#ドメイン名を入力
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:email@example.com
#パスワードを入力
A challenge password []:
An optional company name []:

2つのファイルが作成されている事を確認します。

#サーバ証明書要求ファイル
/etc/ssl/example.com/example-csr.pem
#サーバ秘密鍵
/etc/ssl/example.com/example-key.pem
SAN を含んだ自己署名証明書の作成

Google Chrome 58 から Common Name ではなく SubjectAltName でドメイン名を確認するため、SubjectAltName を含んだ自己署名証明書を作成します。

$cd /etc/ssl/example.com
$sudo sh -c "echo subjectAltName=DNS:example.com > san.ext"

ファイルが作成されている事を確認します。

#SubjectAltName ファイル
/etc/ssl/example.com/san.ext

root権限を使用して署名を行います。

#rootにログインする
$sudo su
#自己承認局で署名
$openssl ca -days 36500 -cert /etc/ssl/example-ca/cacert.pem -keyfile /etc/ssl/example-ca/private/cakey.pem -in /etc/ssl/example.com/example-csr.pem > /etc/ssl/example.com/example.crt -extfile /etc/ssl/example.com/san.ext
#自己認証局のパスワードを入力
Enter pass phrase for /etc/ssl/Hoge-Private-CA/private/cakey.pem:
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n] y
#rootからログアウトする
$exit

ファイルが作成されている事を確認します。

#SubjectAltName を含んだ自己署名証明書ファイル
/etc/ssl/example.com/example.crt
Nginx のインストール
$sudo apt update

$sudo apt-get install -y wget

$sudo apt-get install -y gnupg2

$wget https://nginx.org/keys/nginx_signing.key

$sudo apt-key add nginx_signing.key

$sudo vi /etc/apt/sources.list

# sources.list に追加する 
deb http://nginx.org/packages/ubuntu/ bionic nginx
deb-src http://nginx.org/packages/ubuntu/ bionic nginx

$sudo apt update

$sudo apt install nginx

# Nginx のバージョンが表示される事を確認する
$nginx -v
Nginx の設定

Nginxのサービス起動時に証明書のパスワード入力を求められるため
パスワードを入力したファイルを作成して参照するようにします。

$sudo vim /etc/ssl/example.com/pwf

#パスワードを入力
example

証明書を利用してSSL通信ができるように設定します。

$sudo vim /etc/nginx/conf.d/default.conf

#default.conf を変更
server {
    listen      80;
    listen      443 ssl;
    ssl on;                                                                                                                                                                       
    server_name  example.com;

    ssl_certificate /etc/ssl/example.com/example.crt;
    ssl_password_file /etc/ssl/example.com/pwf
    ssl_certificate_key /etc/ssl/example.com/example-key.pem

Nginx のサービスを開始します。
証明書のアクセス権が無いとエラーになるのでroot権限で実行しています。

#rootにログインする
$sudo su

#設定ファイルのテスト
$nginx -t

#サービスの開始
$systemctl start nginx

#rootからログアウトする
$exit
hostファイルの編集

DNSサーバの代わりにhostファイルを使用して名前解決をしました。
Windows10 の場合は C:\Windows\System32\drivers\etc にあります。
f:id:knkomko:20210503013032p:plain:w450

サーバのIPとサーバ証明書に入力していたドメイン名を入力します。

<サーバのIP>          example.com

f:id:knkomko:20210503013225p:plain:w450

SSLの確認

SSLに対応している事が確認できました。
f:id:knkomko:20210503013720p:plain:w450

DockerでSQL Serverのデータベースをリストアして起動する

f:id:knkomko:20210430223349p:plain

はじめに

これまでSQL Serverを利用した開発は、EC2インスタンスを使用していました。
Dockerを使うと手元の環境で SQL Server を起動して開発を行う事が出来るので
今回はDockerの利点である環境の自動構築を行い、接続までの手順をまとめます。

開発環境

・Widnows10 Pro
・Docker Engine Community Version 20.10.5

全体像

docker compose コマンドで環境の自動構築を行う時にSQL ServerのバックアップファイルをリストアすることでEC2利用時と同じデータベースを再現します。

f:id:knkomko:20210430223815p:plain

使用するファイル
C:\DOCKER\SAMPLE-MSSQL
│  docker-compose.yml
│
├─ mssql
│      db-init.sh
│      ddl.sql
│      Dockerfile
│      entrypoint.sh
│
├─ mssql-backup
│      sampledb.bak
│
└─ mssql-data
docker-compose.yml
version: '3'

services:
   db:
     build: 
      context: ./mssql/
      dockerfile: ./Dockerfile
     container_name: sample-mssql-2017
     ports:
      - "1433:1433"
     environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=Sa@mple@dm1n
      - MSSQL_PID=Express
      - MSSQL_LCID=1041
      - MSSQL_COLLATION=Japanese_CI_AS
      - PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
     volumes:
      - ./mssql-data:/var/opt/mssql
      - ./mssql-backup:/var/opt/mssql/data/backup
Dockerfile
FROM mcr.microsoft.com/mssql/server:2017-latest

COPY ./entrypoint.sh /usr/src/entrypoint.sh
COPY ./db-init.sh /usr/src/db-init.sh
COPY ./ddl.sql /usr/src/ddl.sql

WORKDIR /usr/src/

RUN chmod +x /usr/src/db-init.sh

CMD /bin/bash ./entrypoint.sh
entrypoint.sh

デバッグし易いように db-init.sh の実行ログを log.txt として残しています。

 ./db-init.sh >> /var/opt/mssql/data/backup/log.txt 2>&1 & /opt/mssql/bin/sqlservr
db-init.sh

25s の待ち時間はリストアを行う為にSQL Serverが起動するまで待ちます。
10s の待ち時間はデータベースユーザを追加する為にリストアの終了を待ちます。

/bin/sleep 25s

touch /var/opt/mssql/data/sampledb.mdf
touch /var/opt/mssql/data/sampledb_Log.ldf

/opt/mssql-tools/bin/sqlcmd -H localhost -U sa -P S@mple@dm1n -Q "RESTORE DATABASE sampledb FROM DISK = '/var/opt/mssql/data/backup/sampledb.bak' WITH  FILE = 1, STATS = 5, REPLACE, MOVE 'sampledb' TO '/var/opt/mssql/data/sampledb.mdf', MOVE 'sampledb_Log' TO '/var/opt/mssql/data/sampledb_Log.ldf'"

/bin/sleep 10s

/opt/mssql-tools/bin/sqlcmd -H localhost -U sa -P S@mple@dm1n -i /usr/src/ddl.sql
ddl.sql
EXECUTE AS LOGIN = 'sa'

USE sampletable
GO

DROP USER SampleUser

CREATE USER SampleUser  for login SampleUser

ALTER USER SampleUser 
    WITH DEFAULT_SCHEMA = dbo
GO

GRANT CONNECT TO SampleUser
GO

exec sp_addrolemember 'db_owner', SampleUser
GO
SQL Server の起動
docker compose up --build
SQL Server の接続

SQL Server Management Studio を使用して接続ができることを確認しました。
f:id:knkomko:20210430221003p:plain

C#から踏み台サーバを経由してSQL Serverに接続する

はじめに

複数人による開発でSQL Serverに接続できないかと考えました。
単純にEC2のポートを開けると不正にログインを試みるbotに耐えられずSQL Server が停止しまうため、踏み台サーバを経由した接続を行うことにしました。

全体像

A : 踏み台サーバ
B : データベースサーバ
f:id:knkomko:20210325072334p:plain

A : 踏み台サーバ

SSH接続をする22番ポートを開けています。
またSSH接続用アカウントを新規作成しています。
f:id:knkomko:20210326042123p:plain

今回はSSH.NETを使用してポートフォワーディングを行うため、踏み台サーバではポートフォワーディングの設定は行っていません。
f:id:knkomko:20210324014425p:plain

B : データベースサーバ

SQL Serverの規定インスタンスに接続する1433番ポートを開けています。

ソースコード

C#からSSH接続が行えるSSH.NETを使用しました。
github.com

ソースコードはこちらです。
github.com

SSH.NETでは秘密鍵も利用可能です。

// 踏み台サーバのホスト名
string bastionServer = "XXXXXXXXXX";
// 踏み台サーバのアカウント名
string userName = "XXXXXXXXXX";
// 踏み台サーバのパスワード
string password = "XXXXXXXXXX";

ConnectionInfo info = new ConnectionInfo(bastionServer, 22, userName,
    new AuthenticationMethod[] {
         new PasswordAuthenticationMethod(userName, password)
    }
);

ForwardedPortLocal からポートフォワーディングを行っています。

// SQL Server 接続文字列
string connectionString = @"Data Source=127.0.0.1;Integrated Security=False;User ID=XXXXXXXXXX;Password=XXXXXXXXXX";
// データベースサーバのホスト名
string dbServer = "XXXXXXXXXX";

using (var client = new SshClient(info))
{
    client.Connect();
    var forward = new ForwardedPortLocal("127.0.0.1", 1433, dbServer, 1433);
    client.AddForwardedPort(forward);
    forward.Start();
    using (var connection = new SqlConnection(connectionString))
    {
        using (var command = connection.CreateCommand())
        {
            try
            {
                connection.Open();

                connection.Open();

                command.CommandText = @"SELECT count(*) AS count FROM employee";
結果

踏み台サーバを経由してSQL Server に接続する事ができました。
f:id:knkomko:20210326040108p:plain

スマホにある子供の写真を親のLINEへ定期的に送ってみた

f:id:knkomko:20210321123702p:plain

はじめに

コロナの影響で実家に帰れない為、子供の写真を親のLINEへ送っています。
しかし段々とLINEから送る事を手間に感じてしまい、送り忘れも増えました。

画像共有アプリの利用も考えましたが、アプリのインストール方法から親に説明する必要があるため、新しいアプリの利用は現実的な方法ではありません。

そこで写真を撮るだけで親のLINEに送れないかと考え、取り組むことにしました。
また無料で使えるサービスを組み合わせて実現することを目標にしました。

料金

LINEに1日1枚の画像を送る程度であれば無料の範囲で実現できました。

容量15GBまで無料で使えます。
Google のストレージを追加購入する - パソコン - Google ドライブ ヘルプ

APIも無料で使えます。
Google Drive Apis の利用に関して

  • LINE Messaging API

フリープランを使用していて無料です。
f:id:knkomko:20210321202737p:plain
料金プラン|LINE for Business

サービス間の連携

f:id:knkomko:20210321154153p:plain

①画像を読み込む
子供の写真を撮影するスマホGoogleフォトのアプリをインストールしています。
同期の設定によりスマホ内の写真はGoogleフォトのクラウドストレージに自動でバックアップされるので、Google Apps Scriptでその画像を毎朝9時に読み込みます。

②画像をコピー

Googleアカウントを持たない人にも共有する一般公開のフォルダにGoogleフォトの画像をコピーします。
一般公開とすることでLINEに画像の表示を可能にしています。

③メッセージの送信
Googleフォトにコピーした画像のURLをLINEに送信します。
画像のURLは外部から閲覧できるよう部分的に変更をしています。

④画像の表示
LINEを開くとGoogleドライブに保存していた画像が表示されます。

学び

Googleドライブ
LINEに画像を表示するにはドライブのURLを変更する必要がありました。
ドライブのURLから画像にアクセスできるURLに変更しています。

var downloadURL = buff.getUrl().replace("/file/d/", "/uc?id=").replace("/view?usp=drivesdk", "")

【変更前】
https://drive.google.com/file/d/<ファイルID>/view?usp
【変更後】
https://drive.google.com/uc?id=<ファイルID>

②ダウンロードリンク
画像のURLに "=d" を付加すると画像のダウンロードリンクになります。
LINEで送った画像のダウンロードを可能にする為に必要でした。
f:id:knkomko:20210321193336p:plain

③LINE Messaging API
画像を送るには imageThumbnail と imageFullSize を使用します。
変数downloadURLにGoogleドライブの画像のURLを格納しています。

      var token = ["XXXXXXXXXXXXXXXX"]; //LINEで自動通知をする宛先のトークン。
      var options =
      {
        "method"  : "post",
        "payload" : {"message": "Today's photo.",
                      "imageThumbnail" : downloadURL,
                      "imageFullsize"  : downloadURL,
                    }, 
        "headers" : {"Authorization" : "Bearer "+ token}
      };
    
      UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options); 
結果

僕自身はLINEを使う事が無くなり写真を撮るだけで良くなりました。
なにより我が子の成長を母と兄に見てもらえて嬉しく思っています。

f:id:knkomko:20210321125528p:plain:w350

Run MVP sample app on the Android emulator

f:id:knkomko:20201230045042p:plain

はじめに

2021年1月にAndroidアプリを開発する予定ができました。
何も知らない状態から20日で何が開発できるのか見当もつきませんが
まずはMVPアーキテクチャを学ぶことにしました。

MVPを選択した理由はMVCと似ていて分かりやすかったためです。
eh-career.com

MVPのサンプルをエミュレータで実行しようとしたのですが
一筋縄にはいかなかったため実行にあたり操作した内容をまとめます。

Download MVP sample app

リポジトリからソースコードをダウンロードします。
github.com

SDK

architecture-sample の targetSdkVersion を確認します。
f:id:knkomko:20201230035457p:plain

必要に応じてSDKをインストールします。
f:id:knkomko:20201230035726p:plain

Android emulator

バーチャルデバイスを追加します。
f:id:knkomko:20201230040047p:plain

適当なものを選択します。
f:id:knkomko:20201230040452p:plain

SDKに対応したシステムイメージをダウンロードします。
f:id:knkomko:20201230040520p:plain

ダウンロード後は順に進んで完了です。
f:id:knkomko:20201230040725p:plain

Android emulator HYPERVISOR_ERROR

エミュレータを実行すると HYPERVISOR_ERROR が発生する場合の対策です。
f:id:knkomko:20201230041244p:plain

詳細な内容は以下を参照してください。
qiita.com

Qiitaを参考に私が実際に操作した内容になります。
プログラムと機能の削除 -> Windwosの機能の有効化または無効化
f:id:knkomko:20201230040902p:plain

Hyper-VWindowsハイパーバイザープラットフォーム のチェックを外します。
[OK]ボタンを押した後、変更を適用させるためにWindowsを必ず再起動します。
f:id:knkomko:20201230041051p:plain

Android Studio を起動するとエミュレータの設定画面が表示されました。
メモリサイズは規定値の4GBを選択して次へ進みました。
f:id:knkomko:20201230043822p:plain

インストール後のログにエラーが無いことを確認して完了です。
f:id:knkomko:20201230044129p:plain

Encryption unsuccessful

エミュレータを実行すると Encryption unsuccessful になる場合の対策です。
f:id:knkomko:20201230042459p:plain

tratail の回答が参考になりました。
teratail.com

使用するエミュレータで Wipe Data を実行します。
f:id:knkomko:20201230042914p:plain

Run MVP sample app

MVPのサンプルを実行することが出来ました。
f:id:knkomko:20201230044337p:plain:w300

EC2 Ubuntu に Windows Subsystem for Linux (WSL1) からSSH接続する

f:id:knkomko:20201221012630p:plain

はじめに

年末年始の休みを活用して改めてサーバ構築の勉強をしたいと思い
UbuntuWindows から SSH 接続できるようにしたので書き残します。

Ubuntu は EC2 に作成をしました。

SSH接続

調べてみると Windows から SSH 接続は簡単に出来るようです。

・Traterm
eng-entrance.com

SSHクライアント
dev.classmethod.jp

Windows Subsystem for Linux
www.atmarkit.co.jp

今回は WindowsUbuntu が使える機能 Windows Subsystem for Linux (WSL1) をインストールして SSH 接続に使用することにしました。
WSLがあれば毎回 SSH 接続をしなくてもローカルでコマンドの確認が行えます。

Ubuntu EC2 インスタンスの作成

キーペアの作成までこちらのサイトが参考になりました。
www.acrovision.jp

Windows Subsystem for Linux インストール

インストール手順についてはこちらの記事が分かりやすかったです。
qiita.com

Ubuntu EC2 インスタンス SSH 接続

WSL から ssh コマンドを使用する事で接続が可能でした。

ssh -i {秘密鍵}.pem ubuntu@{インスタンスのIPv4パブリックIP}

pemはEC2インスタンス作成時にダウンロードしたキーペアを指定します。

原因は分からなかったのですが接続の確立が出来ないというメッセージ後に
再試行を許可することで SSH 接続が成功しました。

f:id:knkomko:20201221010907p:plain

Setup DB First EntityFrameworkCore SQL Server

f:id:knkomko:20201220181736p:plain

はじめに

.Net Core で EntityFramework を使う際に .NET Standard と違って試行錯誤したのでDBファーストで使用した手順をまとめます。

対象のフレームワーク

今回は.NET Core 3.1を使用しました。
f:id:knkomko:20201220173109p:plain

開発者用 PowerShell

コマンドは全て開発者用PowerShellから実行します。

カレントディレクトリはプロジェクトルートです。

PS C:\develop\WebApplication1\WebApplication1> dir


    ディレクトリ: C:\develop\WebApplication1\WebApplication1


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/12/20     17:30                bin
d-----       2020/12/20     17:30                obj
d-----       2020/12/20     17:29                Properties
-a----       2020/12/20     17:29            162 appsettings.Development.json
-a----       2020/12/20     17:29            192 appsettings.json
-a----       2020/12/20     17:30            723 Program.cs
-a----       2020/12/20     17:30           1295 Startup.cs
-a----       2020/12/20     17:29            148 WebApplication1.csproj
dotnet ef ツールのインストール

.NET Core 3.0 以降は .NET SDKdotnet ef ツールが含まれていないため
初めて使用する場合はインストールが必要です。

dotnet tool update --global dotnet-ef
Microsoft.EntityFrameworkCore.Design の追加

スキャフォールディングに必要な Design パッケージを追加します。

dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer の追加

スキャフォールディングに必要なデータベースプロバイダーを追加します。

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
Models フォルダの作成

コードを保存する Models フォルダを作成します。
f:id:knkomko:20201220180522p:plain

スキャフォールディング

データベースから DBContext と Entity のコードを Models フォルダに作成します。

dotnet ef dbcontext scaffold "Server=[ホスト名];Database=[データベース名];persist security info=True;user id=[ユーザーID];password=[パスワード];MultipleActiveResultSets=True" Microsoft.EntityFrameworkCore.SqlServer -o Models
確認

EntityFrameworkCoreを使用してデータベースからコードが作成できました。

f:id:knkomko:20201220180806p:plain

参考資料

docs.microsoft.com