Jellyfinをhttpsで使いたい（オレオレ証明書）

# はじめに
最近、JellyfinをLinuxMint上に構築しました。それは問題なく動作しているのですが、http通信のため暗号化されておらず、Wiresharkなどのソフトを使えば簡単に通信内容を見ることができるため不安です。そこで、https通信ができるように設定していきたいと思います。

:::note
~~この方法でSSL化を行うとjellyfin公式クライアントからアクセスできなくなり、完全にウェブブラウザ専用になってしまいます。もしかすると、オレオレ証明書だからブロックされてるのかも？~~
(2025/02/09 更新)　証明書を作成する際にSANを含め、作成した証明書を端末にインストールすることで正常に公式クライアントから読み込めるようになりました。
:::

# そもそもJellyfinってなんだよ？
Jellyfinとは、完全に無料でオープンソースなメディアサーバーソフトウェアです。いろいろな形式のメディア（動画、写真、電子書籍、音楽など）に対応しており、またクライアントソフトが様々なデバイス上で動作することが魅力です。
詳しくは↓のリポジトリのREADMEを参照。

https://github.com/jellyfin/jellyfin

# 環境
ノートパソコンなのでかなり低スぺですがJellyfinは快適に動作します。
| 種類 | 名前 |
|:-:|:-:|
| OS  | Linux Mint 20 (ulyana)  |
| CPU  | Intel(R) Celeron(R) CPU N3450 @ 1.10GHz  |
| ストレージ容量  |  256GB  |
| メモリ(RAM)  |  8GB  |
| Jellyfin | 10.10.3 |

# 手順
既にJellyfinはセットアップ済みであることを前提に話を進めます。
まだ済んでいない方は公式サイトの[インストールスクリプト](https://jellyfin.org/docs/general/installation/linux/)を使用すると一瞬でインストールできるのでお勧めです。
また、ここからの手順は以下の記事を参考に作成しています。

https://qiita.com/cffnpwr/items/09995c5b19b01d7f1092

## OpenSSLをインストール
```sh
sudo apt install openssl
```
```:実行結果
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
openssl はすでに最新バージョン (1.1.1f-1ubuntu2.23) です。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 5 個。
```
私の環境では既にOpenSSLがインストールされていました。もしかするとJellyfinのインストールスクリプト内にOpenSSLをインストールするコマンドが書かれているのかも？

## 一時的に管理者権限を取得する
これからの作業のためには管理者権限がないといけません。
```sh
sudo su
```

## /etc/ssl/privateに移動する
ここで秘密鍵を置いておきたいディレクトリに移動します。
```sh
cd /etc/ssl/private
```

## 秘密鍵を作成する
ここら辺いろいろ設定できそうですが、よくわからないので元記事のコマンドをそのまま実行します。この秘密鍵を使用してサーバー証明書を作成するためのデータであるCSRを作成します。......自分で書いててよくわからない()
```sh
openssl genrsa -aes256 -out server.key 4096
```
```:実行結果
Generating RSA private key, 4096 bit long modulus (2 primes)
........................................................................................................................................................................................................++++
...............................................................................................................++++
e is 65537 (0x010001)
Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:
```
途中でパスワードを設定する必要があります。

## CSRを作成する
>CSR（Certificate Signing Request）は、証明書署名要求と呼ばれ、SSL/TLS証明書を取得する際に必要な重要な要素です。

ってperplexityが言ってました。なんかいろいろな情報を書いておくテキストファイルで、これを使って証明書が作られます。(たぶん)
```sh
openssl req -new -key server.key -out server.csr
```
```:実行結果（国名などは仮置きです。）
Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: JP
State or Province Name (full name) [Some-State]: Tokyo
Locality Name (eg, city) []: Minato-ku
Organization Name (eg, company) [Internet Widgits Pty Ltd]: Karin Solutions Inc.
Organizational Unit Name (eg, section) []: IT Department
Common Name (e.g. server FQDN or YOUR name) []: TanakaTaro
Email Address []: example@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
```

```:日本語訳
サーバーキーのパスフレーズを入力してください:
証明書リクエストに組み込まれる情報を入力するよう求められます。
これから入力するのは、Distinguished Name または DN と呼ばれるものです。
いくつかの項目がありますが、空白のままにしておくこともできます。
一部の項目にはデフォルト値があります。
'.' を入力すると、その項目は空白のままにされます。

国名（2文字のコード）[AU]: JP
都道府県名（正式名称）[Some-State]: Tokyo
市区町村名（例: city）[]: Minato-ku
組織名（例: company）[Internet Widgits Pty Ltd]: Karin Solutions Inc.
部門名（例: section）[]: IT Department
共通名（例: サーバーFQDN または あなたの名前）[]: TanakaTaro
メールアドレス []: example@gmail.com

証明書リクエストに送信される以下の「追加」属性を入力してください:
チャレンジパスワード []:
任意の会社名 []:
```

はじめにさっき設定したパスワードを入力する必要があります。
また、チャレンジパスワードとかはよくわからなかったので空白にしました。

## SANを作成する（2025/02/09 追加）
証明書を作成する際にSANというものを含め、それをjellyfinのクライアント側の端末にインストールすることで公式クライアントが動作するようになりました。以下はSANの作成方法です。

SANを作成し、編集します。エディタはなんでも構いません。
```sh
sudo nano san.txt
```
中身を以下のように編集します。subjectAltNameのIPアドレスは各々のものに読み替えてください。
ここで重要なのが、「basicConstraints=CA:TRUE」の部分です。ここをTRUEにしておくことで、Android端末に証明書をインストールする際にエラーが出ないようになります。
```sh
subjectAltName = IP:192.168.0.93
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:TRUE
keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
```

## 証明書を作成
ついに証明書を作成します！
```sh
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out ../certs/server.crt -extfile san.txt
```
またパスワードを入力する必要があります。
```:実行結果
Signature ok
subject=C = JP, ST = Tokyo, L = Minato-ku, O = Karin Solutions Inc., OU = IT Department, CN = TanakaTaro, emailAddress = example@gmail.com
Getting Private key
Enter pass phrase for server.key:
```
## （任意）Android端末にインストールする用の証明書を作成する
私は主にAndroidでJellyfinを使用する予定で、またできればブラウザではなく公式クライアントで使えるようにしたいです。そのために必要な証明書を作成します。（正確には先ほど作成した証明書をAndroidで読み込める形式に変換しています。）
```
openssl x509 -in ../certs/server.crt -out ../certs/server.der.crt -outform der
```
ここで出力されたserver.der.crtを端末にインストールすることで、公式クライアントを動作させられるようになります。（導入方法は各自で調べてください。）

## パスフレーズを削除する
nginxで使用するのにパスフレーズがあると邪魔なので鍵から削除します。
検証していませんが、そもそも最初からパスフレーズを付ける必要なんてなかったのかもしれません。
まぁ動くのでヨシ！
```sh
openssl rsa -in /etc/ssl/private/server.key -out /etc/ssl/private/server.key
```
```:実行結果
Enter pass phrase for /etc/ssl/private/server.key:
writing RSA key
```

## nginxをインストールする
まだこれだけじゃダメなんです。次に、リバースプロキシとやらを作る必要があります。
今回はnginxをリバースプロキシにするので、nginxをインストールします。
ここからは以下の記事を参考にします。

https://qiita.com/schwarz471/items/9b44adfbec006eab60b0

https://qiita.com/tnoce/items/ded6d3d298da5972ab63

```sh
apt install nginx
```

<details><summary>実行結果</summary>

```:実行結果
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
  libnginx-mod-http-image-filter libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream nginx-common
  nginx-core
提案パッケージ:
  fcgiwrap nginx-doc
以下のパッケージが新たにインストールされます:
  libnginx-mod-http-image-filter libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream nginx
  nginx-common nginx-core
アップグレード: 0 個、新規インストール: 7 個、削除: 0 個、保留: 5 個。
604 kB のアーカイブを取得する必要があります。
この操作後に追加で 2,141 kB のディスク容量が消費されます。
続行しますか? [Y/n]
取得:1 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 nginx-common all 1.18.0-0ubuntu1.6 [37.7 kB]
取得:2 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 libnginx-mod-http-image-filter amd64 1.18.0-0ubuntu1.6 [14.8 kB]
取得:3 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 libnginx-mod-http-xslt-filter amd64 1.18.0-0ubuntu1.6 [13.0 kB]
取得:4 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 libnginx-mod-mail amd64 1.18.0-0ubuntu1.6 [42.8 kB]
取得:5 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 libnginx-mod-stream amd64 1.18.0-0ubuntu1.6 [67.2 kB]
取得:6 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 nginx-core amd64 1.18.0-0ubuntu1.6 [425 kB]
取得:7 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 nginx all 1.18.0-0ubuntu1.6 [3,616 B]
604 kB を 2秒 で取得しました (297 kB/s)
パッケージを事前設定しています ...
以前に未選択のパッケージ nginx-common を選択しています。
(データベースを読み込んでいます ... 現在 336724 個のファイルとディレクトリがインストールされています。)
.../0-nginx-common_1.18.0-0ubuntu1.6_all.deb を展開する準備をしています ...
nginx-common (1.18.0-0ubuntu1.6) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-image-filter を選択しています。
.../1-libnginx-mod-http-image-filter_1.18.0-0ubuntu1.6_amd64.deb を展開する準備をしています ...
libnginx-mod-http-image-filter (1.18.0-0ubuntu1.6) を展開しています...
以前に未選択のパッケージ libnginx-mod-http-xslt-filter を選択しています。
.../2-libnginx-mod-http-xslt-filter_1.18.0-0ubuntu1.6_amd64.deb を展開する準備をしています ...
libnginx-mod-http-xslt-filter (1.18.0-0ubuntu1.6) を展開しています...
以前に未選択のパッケージ libnginx-mod-mail を選択しています。
.../3-libnginx-mod-mail_1.18.0-0ubuntu1.6_amd64.deb を展開する準備をしています ...
libnginx-mod-mail (1.18.0-0ubuntu1.6) を展開しています...
以前に未選択のパッケージ libnginx-mod-stream を選択しています。
.../4-libnginx-mod-stream_1.18.0-0ubuntu1.6_amd64.deb を展開する準備をしています ...
libnginx-mod-stream (1.18.0-0ubuntu1.6) を展開しています...
以前に未選択のパッケージ nginx-core を選択しています。
.../5-nginx-core_1.18.0-0ubuntu1.6_amd64.deb を展開する準備をしています ...
nginx-core (1.18.0-0ubuntu1.6) を展開しています...
以前に未選択のパッケージ nginx を選択しています。
.../6-nginx_1.18.0-0ubuntu1.6_all.deb を展開する準備をしています ...
nginx (1.18.0-0ubuntu1.6) を展開しています...
nginx-common (1.18.0-0ubuntu1.6) を設定しています ...
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service.
libnginx-mod-http-xslt-filter (1.18.0-0ubuntu1.6) を設定しています ...
libnginx-mod-mail (1.18.0-0ubuntu1.6) を設定しています ...
libnginx-mod-http-image-filter (1.18.0-0ubuntu1.6) を設定しています ...
libnginx-mod-stream (1.18.0-0ubuntu1.6) を設定しています ...
nginx-core (1.18.0-0ubuntu1.6) を設定しています ...
nginx (1.18.0-0ubuntu1.6) を設定しています ...
systemd (245.4-4ubuntu3.24) のトリガを処理しています ...
man-db (2.9.1-1) のトリガを処理しています ...
ufw (0.36-6ubuntu1.1) のトリガを処理しています ...
```

</details>

## 設定ファイルのディレクトリに移動する
これからnginxの設定ファイルを作るので、設定ファイルを置くディレクトリに移動してください。
```sh
cd /etc/nginx/conf.d
```

## 設定ファイルを作る
新しく設定ファイルを作成します。server.confとしてファイルを作成し、
```sh
nano server.conf
```
以下のように編集して保存してください。
```conf:server.conf
server {
    listen 443 ssl;
    server_name localhost;

    # SSL証明書と鍵の設定
    ssl_certificate /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;

    # 推奨されるSSL設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # エラーページの設定
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }

    # プロキシ設定
    location / {
        proxy_pass http://localhost:8096; # 内部Webサーバーへのプロキシ
        proxy_set_header Host $host;
        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;
    }
}
```

## nginxを再起動する
```sh
systemctl restart nginx.service
```
```:実行結果
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
'nginx.service'を再起動するには認証が必要です。
Authenticating as: Server,,, (server)
Password:
==== AUTHENTICATION COMPLETE ===
```
設定ファイルがミスってたりするとここでエラーが出ます。
一応以下のコマンドで、正常動作しているか確認しておいたほうがいいと思います。
```sh
systemctl status nginx.service
```
```:実行結果
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2025-01-21 19:39:28 JST; 7s ago
       Docs: man:nginx(8)
    Process: 6787 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SU>
    Process: 6798 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 6799 (nginx)
      Tasks: 5 (limit: 9221)
     Memory: 5.3M
     CGroup: /system.slice/nginx.service
             ├─6799 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─6800 nginx: worker process
             ├─6801 nginx: worker process
             ├─6802 nginx: worker process
             └─6803 nginx: worker process

 1月 21 19:39:28 server systemd[1]: Starting A high performance web server and a reverse proxy serv>
 1月 21 19:39:28 server systemd[1]: Started A high performance web server and a reverse proxy serve>
lines 1-18/18 (END)
```
よくわかんなかったらとりあえず緑色になってることだけ確認していただければ。

# 完成！
これで設定は終わりです！https://(サーバーのipアドレス):443にアクセスすると、しっかりログイン画面が表示されるはずです。お疲れさまでした。
後はファイヤーウォールで外部からの8096ポートへのアクセスを禁止すれば、sslでしかアクセスできないjellyfinサーバーが完成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1751564/810838a7-c89b-b1d5-ff0b-70b42dd81728.png)

# おわりに
今回はjellyfinをssl化してみました。ボタンポチポチで終わるかもと思っていたら、意外にもめんどくさい手順を踏む必要があり大変でした。いつか気が向いたらLet's Encryptも試してみたいと思います。
疲れたぁ.......