Chocolateyのサーバーを爆速でLAN内に構築

# あらすじ

B「`choco`してますか？」
A「めっちゃ使いたいけどさ、ネット接続してないPCがあって使えないよ」
B「それ、ChocolateyのサーバーをLAN内に作ったら解決するよ」
※結論からいうとそんな簡単ではありません。後述します。

A「え～？いろいろ面倒なんでしょ？」
B「そうでもないよ、大きな流れが見たいだけなら」

Freeプランの環境で、爆速に環境を構築できる方法のハンズオンです。

# 推奨前提条件

- powershellでコマンドレット実行の方法が分かる
- `choco install` コマンドを使ったことがある
- Windows10を使っている
- 自由に使えるPCが2台以上ある
- LANケーブルやHUBをつなぎ変えることが出来る

# なぜ内部ホストを構築したいの？

Chocolatey.orgが運営するコミュニティリポジトリには利用制限がかかっています。
5回/minを超えて`choco install`コマンドを使おうとすると１時間ロックダウンされます。
これを避けるには専用ホストの構築が必須となります。

また、[WSUS](https://docs.microsoft.com/ja-jp/windows-server/administration/windows-server-update-services/deploy/deploy-windows-server-update-services)やフォワードプロキシと同様にインターネット回線の負荷分散が出来ることもメリットの一つです。

# 基本構成

- サーバーPC
    - `chocolatey.server`をインストール
    - Windows10 Pro
- クライアントPCs
    - Windows10 Pro

※爆速で試すなら、本質的はサーバーPCへ`chocolatey.server`をインストールするだけで完了です。
※爆速で試すなら、サーバーOSでなくても大丈夫です。本番はServerOSにした方がコンプライアンス的に良いです。
そのあと細かい設定を行います。

# 作業の大筋

本稿はこの流れをハンズオンすることが目標です。

1. サーバーPCで内部サーバーを準備
2. クライアントPCで準備
3. クライアントPCからパッケージを内部サーバーに置く（choco push）
3. クライアントPCでインストールソースに内部サーバーを追加（choco source add）
4. クライアントPCで内部サーバーからパッケージをインストール(choco install)
5. 喜びを誰かと分かち合う

※push, installはサーバー内でやっても大丈夫です。ただし、より本番環境に寄せるなら、別PCからやった方が良さそう。

# チョコレートサーバー構築手順

# チョコレートサーバー構築手順

コマンド`choco`が使えないと始まりません。chocolateyの[インストールページ](https://chocolatey.org/install)に行ってインストール用ワンライナースクリプトをコピーしてきます。おもむろに「Powershell」を開き、貼り付けてEnterキーを押します。
※この時、管理者権限で実行することを忘れないようにしてください。また、インターネット接続が必要です。

# チョコレートサーバーを設置しよう

[公式サイト](https://chocolatey.org/packages/chocolatey.server)にある通り、サーバーPC内のpowershellで`choco install chocolatey.server`を実行します。※インターネットへの接続が必要です。

## 設置用スクリプト

これを管理者権限のpowershellで実行します。※インターネット接続が必要です。
最新のスクリプトは[prepare-for-internal-use](https://docs.chocolatey.org/en-us/guides/organizations/organizational-deployment-guide#exercise-0-prepare-for-internal-use)ページで確認してください。このページにはインターネット接続が無いサーバーで設定する方法も書かれています。ただし、爆速で試すなら、インターネット接続したほうが速いです。

```posh
# Ensure we can run everything

Set-ExecutionPolicy Bypass -Scope Process -Force

# Setting up directories for values

New-Item -Path "$env:SystemDrive\choco-setup" -ItemType Directory -Force
New-Item -Path "$env:SystemDrive\choco-setup\files" -ItemType Directory -Force
New-Item -Path "$env:SystemDrive\choco-setup\packages" -ItemType Directory -Force

# Install Chocolatey

# Install Chocolatey

# Install Chocolatey

iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

# Are you military, government, or for some other reason have FIPS compliance turned on?

# Are you military, government, or for some other reason have FIPS compliance turned on?

# Add license to setup and to local install

New-Item $env:ChocolateyInstall\license -ItemType Directory -Force
Write-Host "Please add chocolatey.license.xml to '$env:SystemDrive\choco-setup\files'."
Write-Host 'Once you do so, press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
Copy-Item $env:SystemDrive\choco-setup\files\chocolatey.license.xml $env:ChocolateyInstall\license\chocolatey.license.xml -Force

# Install Chocolatey Licensed Extension

choco upgrade chocolatey.extension -y --pre
Write-Host "If you see what looks like an error about a missing extension, that is what this step does so it will clear up on next command."
# C4BTRIAL: Place nupkgs into the "$env:SystemDrive\choco-setup\packages" directory (add a script here to do so)

# C4BTRIAL: Place nupkgs into the "$env:SystemDrive\choco-setup\packages" directory (add a script here to do so)

# Set Configuration

choco config set cacheLocation $env:ALLUSERSPROFILE\choco-cache
choco config set commandExecutionTimeoutSeconds 14400
# TODO: Add other items you would configure here

# TODO: Add other items you would configure here

# Set Licensed Configuration

choco feature enable --name="'internalizeAppendUseOriginalLocation'"
choco feature enable --name="'reduceInstalledPackageSpaceUsage'"
# TODO: Add other items you would configure here

# TODO: Add other items you would configure here

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# TODO: Are we installing the Chocolatey Agent Service? C4B Only

# Download and internalize packages.

choco download chocolatey chocolatey.server dotnet4.6.1 chocolateygui --internalize --output-directory="$env:SystemDrive\choco-setup\packages" --source="'https://chocolatey.org/api/v2/'"
# C4BTRIAL: skip this next step

# C4BTRIAL: skip this next step

choco download chocolatey.extension chocolatey-agent --internalize --output-directory="$env:SystemDrive\choco-setup\packages" --source="'https://licensedpackages.chocolatey.org/api/v2/'" # C4BTRIAL: skip this next step

# C4BTRIAL: skip this next step

# C4BTRIAL: skip this next step

# Download local install script - need at least PowerShell v3

$installScript = iwr -UseBasicParsing -Uri https://gist.githubusercontent.com/ferventcoder/d0aa1703a7d302fce79e7a4cc13797c0/raw/b1f7bad2441fa6c371b48b8475ef91cecb4d6370/ChocolateyLocalInstall.ps1 -UseDefaultCredentials

$installScript.Content | Out-File -FilePath "$env:SystemDrive\choco-setup\files\ChocolateyLocalInstall.ps1" -Encoding UTF8 -Force
Write-Warning "Check and adjust script at '$env:SystemDrive\choco-setup\files\ChocolateyLocalInstall.ps1' to ensure it points to the right version of Chocolatey in the choco-setup\packages folder."
```

## チョコの噴出口はどこにする？

サーバーアプリケーションは、サーバー外からアクセスするできるようにポート開放を行う必要があります。チョコレートフォンデュのチョコサーバーを作るとき、噴出口を上につけるのか横につけるのかみたいな感じです。

デフォルトではポート80に設定されます。httpsにするために`443`にしたい場合や、`8080`などにして別のサーバーアプリケーションでリバースプロキシしたい場合などがあると思います。また、その場合はEdit bindings でポート変更などします。私の環境では既に別のソフトの検討で`80`が埋まっていたため`8080`に変更しました。

まず、スタートメニューで「IIS」と検索して開きます。そのあと、「Sites」→「chocolateyServer」と辿り、右クリックから「Edit Binding」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134703/6fa00d6f-0148-835c-c7b1-60a8cbf7c142.png)

add（追加）したりEdit（編集）したりします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134703/f5130e26-a563-c3ba-2bd4-bbb9f014af54.png)

# 構築完了

いろいろ設定した後に、他のPCのブラウザからアクセスしてみてください。
URLは`http://server_ip_address:port_number/`です。
こう表示されたら完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134703/15c39861-deff-e6f8-79fc-d2842734c73c.png)
※ちなみに、このページにサーバー内からアクセスすると、管理者情報が標準されます。便利情報が載っていますので、ぜひ活用してみて下さい。

# クライアントの準備

早くチョコレートサーバーからチョコレート漬けのナゲットをダウンロードしてインストールしたいですね。

## チョコを使いたい

まずはクライアントから`choco`を使えるようにしましょう。
さて、先ほどのサーバー設定の時とは、`choco`というコマンドを使えるようにするための手順が違います。
構築したばかりのチョコサーバーにブラウザでアクセスしてみてください。

すると、こんな記述があります。
>Installing Chocolatey On Client Machines
PowerShell.exe - copy and paste the following into the shell and press enter:
(クライアント機でのchocolateyのインストール　powershell - コピペしてenterキーを押してください：)

```posh
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('http://xxx.xxx.xxx.xxx:zzz/install.ps1'))
```

この通りに実行すると、`choco`コマンドが使えるようになります。この時、クライアントにインターネット接続は必要ありません。

# 内部ホストにパッケージをPushする

chocolatey.orgで配布されているアプリケーションは、パッケージになっています。このパッケージはnugetという仕組みです。パッケージをnuget形式で梱包する感覚です。ボランティアの皆さんがパッケージングしてchocolatey.orgにアップロードしているんですね。これをプッシュするといいます。

そして、LAN内に設置したサーバーでも仕組みはchocokatey.orgと同じです。基本的に`choco push`というコマンドでこのサーバーにnugetファイルをプッシュすることになります。
※これを手動でやるには、`c:\tools\chocolatey.server\App_Data\Packages`にnugetファイルをコピーペーストします。

nugetファイルはpackageから事前にダウンロードしておきます。試すだけなので、適当に落としておけばいいですね。
また、pushするpcはインターネット接続していようといまいと関係ありません。される側も同様です。

この時、「インストールしたいパッケージだけダウンロードすればいいか？」というと、そうでもありません。例えば、Teraterm[パッケージのページ](https://chocolatey.org/packages/teraterm#dependencies)では「dependencies」に依存関係が無いとあります。依存関係が無ければ単体で良いでしょう。

しかし、[powershell-preview](https://chocolatey.org/packages/powershell-preview#dependencies)では「dependencies」に依存関係が無いとあります。依存関係が無ければ単体で良いでしょう。

#dependencies)では「dependencies」に依存関係が無いとあります。依存関係が無ければ単体で良いでしょう。

nugetファイルを置いたフォルダでpushする場合

```
choco push --source http://xxx.xxx.xxx.xxx:zzz/chocolatey --api-key chocolateyrocks -force
```

絶対参照でアドレスを指定したい場合

```
choco push directory_path_to_nuget_package --source http://xxx.xxx.xxx.xxx:zzz/chocolatey --api-key chocolateyrocks -force
```

上手くプッシュできるとこうなります。

```
powershell.portable 7.1.0 was pushed successfully to http://xxx.xxx.xxx.xxx:xxx/chocolatey
```

毎回 `--api-key`を指定するのが面倒でサボりたい時は、「このPCこそはpush専用機なんだ、毎回爆速でpush出来なきゃダメだろ」ってPCで下記コマンドを実行しておきます。※長期で利用する場合にはapiキーを変えましょう。

```
choco apikey --source = "http://xxx.xxx.xxx.xxx:zzz/chocolatey" --api-key = chocolateyrocks
```


## --api-key

Apiキーの指定を忘れるとチョコに怒られます。

```
An ApiKey was not found for 'http://xxx.xxx.xxx.xxx:xxx/chocolatey'. You must either set an api key in the configuration or specify one with --api-key.
```

意訳すると、「`http://~`にアクセスするときの愛言葉を忘れてるけど？ちゃんと"--api-key"オプションで教えて！それか事前に別コマンドで伝えてよ！」です。

「Apiキーってなんだよ！」と思いますが、設定ファイル`Web.config`にApiキーが書いてあります。

```configuration:/tools/chocolatey.server/Web.config
<add key="apiKey" value="chocolateyrocks" />
```

そのため、爆速で試すだけであれば、Apiキーには`chocolateyrocks`を指定すればいいと思います。チョコの岩…おいしそう。もし、違うApiキーでも上手く動くか試したい場合には、`Web.config`を書き換えた後、前述のIISでサーバーをrestart(再起動)してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134703/ff628000-c6fa-6392-46af-eca4fe1aaced.png)


## HTTPS : SSL

構築したサーバーにSSL認証を付与できていないとチョコに怒られます。

```
WARNING! The specified source 'http://xxx.xxx.xxx.xxx:zzz/chocolatey' is not secure.
 Sending apikey over insecure channels leaves your data susceptible to
 hackers. Please update your source to a more secure source and try again.

 Use --force if you understand the implications of this warning or are
 accessing an internal feed. If you are however doing this against an
 internet feed, then the choco gods think you are crazy. ;-)

NOTE: For chocolatey.org, you must update the source to be secure.
```

意訳すると、
「あぶねーな！指定したソース`https://xxx~`は安全じゃねーぞ？こんな危ないチャンネルで送信したら、ハッカーにデータをすり替えられっぞ。悪いことは言わねーから、お前のソースをもっと安全にしてから出直してきな。」
「内なる世界へ触れようしておるのか？この予言の意味が分かるのならばフォースを使うのじゃ。しかし、そなたが外界へ触れんとしているのであれば、チョコ神の怒りにふれることであろう:wink:。」
「注意：`chocolatey.org`向けならば、安全になるようにあなたのソースを更新しなければいけません。」
です。

とりあえず爆速したいので、フォースを使います。

## チョコパッケージ（nugetファイル）

チョコパッケージは、拡張子が`.nuget`になっているファイルです。これを指定しないとチョコに怒られます。

```text
No .nupkg files (or more than 1) were found to build in 'C:\Users\xxxxx'. Please specify the .nupkg file or try in a different directory.
```

意訳すると、「ナゲットが１個も無いじゃないか、この場所！ナゲットを渡せよ、それか違う場所に行こうぜ。」

## Forbidden (403)

下記のようにチョコに怒られた時は、ファイアウォール設定を見直しましょう。
下記に`http://xxx.xxx.xxx.xxx:zzz/chocolatey`となっています。
そのため、サーバー上のファイアウォール設定でポート`zzz`をTCPで許可してあげればOKです。
`zzz`が無い場合は、`http`ならポート`80`、`https`ならポート`443`です。

```text
Attempting to push powershell.portable.7.1.0.nupkg to http://xxx.xxx.xxx.xxx:zzz/chocolatey
Failed to process request. 'Forbidden'.
リモート サーバーがエラーを返しました: (403) 使用不可能.
```

意訳すると、「`https://~`にpowershellっていうナゲットをプッシュしようとしてるんだけど、申請ができなかったよ。禁止されちゃった。」です。

# 実際に使ってみる

さて、pushできましたか？
先ほど内部サーバーにpushしたものが使えるのか試してみましょう。

普通なら、下記のコマンドを実行するだけなのですが、今はまだ、パッケージを探しに行く宛て先が外側になっています。
このままだと、<https://chocolatey.org/install>に探しに行ってエラーになるだけです。

```posh
choco install powershell.portable -y
```

## 内部ホストをソースとして追加

そこで、`choco install`のためのソースに、先ほど構築した内部ホストサーバーを追加します。

```posh
choco source add --name=internal_machine --source=http://192.168.0.18:8080/chocolatey
```

下記のように表示されたら、追加できています。

```posh
Added internal_machine - http://192.168.0.18:8080/chocolatey (Priority 0)
```

※この後クライアントPCは、インターネット接続が無く、かつ内部ホストサーバーに接続できるLANに置いて続きを試してください。
もし難しいようでしたら、`choco source remove`コマンドで`https://chocolatey.org/api/v2/`を取り除いておきましょう。
そうしないと内部ホストサーバーを読みに行ってしまいます。

## さあインストールしよう

先ほどpushしたパッケージをインストールしてみましょう。下記のコマンドでできるはずです。

```posh
choco install powershell.portable
```

Enterキーで実行しました。どうなるでしょうか。

```posh
Installing the following packages:
powershell.portable
By installing you accept licenses for the packages.
Error retrieving packages from source 'https://chocolatey.org/api/v2/':
 リモート名を解決できませんでした。: 'chocolatey.org'
```

まずは、`chocolatey.org`につながらないことが分かります。
ここまでは想定内ですね。さて、

```posh
Progress: Downloading powershell.portable 7.1.0... 100%

powershell.portable v7.1.0
powershell.portable package files install completed. Performing other installation steps.
The package powershell.portable wants to run 'chocolateyinstall.ps1'.
Note: If you don't run this script, the installation will fail.
Note: To confirm automatically next time, use '-y' or consider:
choco feature enable -n allowGlobalConfirmation
Do you want to run the script?([Y]es/[A]ll - yes to all/[N]o/[P]rint):
```

無事にダウンロードが行われたようです。
`a`と入力して次に進みましょう。

```posh
Do you want to run the script?([Y]es/[A]ll - yes to all/[N]o/[P]rint): a

Attempt to get headers for https://github.com/PowerShell/PowerShell/releases/download/v7.1.0/PowerShell-7.1.0-win-x64.zip failed.
  The remote file either doesn't exist, is unauthorized, or is forbidden for url 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.0/PowerShell-7.1.0-win-x64.zip'. Exception calling "GetResponse" with "0" argument(s): "The remote name could not be resolved: 'github.com'"
Downloading powershell.portable 64 bit
  from 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.0/PowerShell-7.1.0-win-x64.zip'
ERROR: The remote file either doesn't exist, is unauthorized, or is forbidden for url 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.0/PowerShell-7.1.0-win-x64.zip'. Exception calling "GetResponse" with "0" argument(s): "The remote name could not be resolved: 'github.com'"
The install of powershell.portable was NOT successful.
Error while running 'C:\ProgramData\chocolatey\lib\powershell.portable\tools\chocolateyinstall.ps1'.
 See log for details.

Chocolatey installed 0/1 packages. 1 packages failed.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

Failures
 - powershell.portable (exited 404) - Error while running 'C:\ProgramData\chocolatey\lib\powershell.portable\tools\chocolateyinstall.ps1'.
 See log for details.
```

あれ？上手くいきませんでしたね。
どうやら、パッケージ内で、<https://github.com>にアクセスしているインストールスクリプトがあるようです。
これは残念です。このソフトの管理にchocolateyを使うのををあきらめるか、自作パッケージを作成するしかないようですね。

# まとめ

インターネット接続が前提のパッケージ設計だと、内部ホストしているメリットが激減するようです。
「自作パッケージを作る」か、「管理しているクライアントPCはすべてインターネット接続する」という選択が必要です。

そもそも、ソースに内部ホストを追加した時、デフォルトの設定で、<https://chocolatey.org>が優先されます。ここから推測できるのは、インターネット接続が当たり前であり、<https://chocolatey.org>に置いていない独自パッケージをホストすることが目的なのではないか？ということです。※公式ドキュメントを読み込むか公式に質問すれば真偽が分かると思われます。

**Excelsior!**