Azure DevOps
Azure Artifacts のプライベートフィードにある Babel Obfuscator の NuGet パッケージを参照し、ライセンスをシークレット変数として渡して、Azure Pipelines で Babel Obfuscator を実行します。
Azure Pipelines は .NET SDK でプロジェクトをビルドし、Babel.Obfuscator パッケージがそのビルドに Babel タスクを組み込みます。ビルドエージェントに Babel をインストールする必要はありません。ビルドツールはパッケージに含まれているため、パイプラインは、パッケージを置いたフィードに対して認証し、Babel にライセンスを渡すだけで済みます。
このページで使用するサンプルは、小さなコンソールアプリケーションです。
git clone https://github.com/babelfornet/devops-integration.gitパッケージを Azure Artifacts フィードに置く
Babel.Obfuscator パッケージは nuget.org では公開されていません。Ultimate エディションまたは Babel Licensing のサイトエディションと一緒に提供されます。このパッケージは、自分の組織だけが読み取れるプライベートフィードにプッシュしてください。
Babel Obfuscator のパッケージをパブリックフィードには決してプッシュしないでください。パッケージには Babel のビルドツールが含まれており、再公開するとライセンスの条件に違反します。
Azure DevOps で「Artifacts」を開き、Babel という名前のフィードを作成して、そこにパッケージをプッシュします。
dotnet nuget push Babel.Obfuscator.12.0.0.nupkg \
--source "https://pkgs.dev.azure.com/ORGANISATION/_packaging/Babel/nuget/v3/index.json" \
--api-key azソリューションと同じ場所に NuGet.config を追加し、自分のコンピューターとビルドエージェントの両方がこのフィードからパッケージを解決できるようにします。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="babel" value="https://pkgs.dev.azure.com/ORGANISATION/_packaging/Babel/nuget/v3/index.json" />
</packageSources>
</configuration>このファイルには、意図的に資格情報を含めていません。開発者のコンピューターでは Azure Artifacts Credential Provider が一度だけ資格情報の入力を求め、パイプラインでは NuGetAuthenticate タスクがビルド ID のトークンを渡します。どちらの場合も、リポジトリにシークレットが書き込まれることはありません。
パッケージを参照する
アセンブリを難読化したいすべてのプロジェクトに、パッケージ参照を追加します。
<ItemGroup>
<PackageReference Include="Babel.Obfuscator" Version="12.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>PrivateAssets を指定すると、プロジェクトが生成するパッケージにこの参照が含まれなくなります。Babel はビルド時のツールであり、配布するアセンブリの依存関係ではないためです。
ライセンスをシークレットとして渡す
期限なく動作するアセンブリを生成するには、Babel にライセンスが必要です。ライセンスがない場合、Babel は評価モードで実行され、出力は短期間で動作しなくなります。
babel.licenses をリポジトリにコミットしないでください。リポジトリ内のライセンスファイルは、そのリポジトリを読み取れるすべての人と、フォークしたすべての人が読み取れます。ファイルを削除した後でも同じです。その blob は、フォークと元のリポジトリの履歴の中で引き続き参照できるからです。
代わりに、ライセンスキーをシークレット変数として保存します。パイプラインの「Variables」で BabelLicense を追加し、シークレットとして指定します。次に、ビルドステップでこれを環境変数にマッピングし、プロジェクトから読み取ります。
<PropertyGroup Condition="'$(BABEL_LICENSE)' != ''">
<BabelLicense>$(BABEL_LICENSE)</BabelLicense>
</PropertyGroup>BabelLicense プロパティには、ライセンスファイルのパス、ライセンスキー、または floating:<user key> の形式で書いたフローティングライセンスのユーザーキーを指定できます。ビルドエージェントでは、後の 2 つを使用します。どちらもディスク上のファイルを必要としないためです。ローカルではこのプロパティを設定しないままにしておくと、Babel はプロジェクトフォルダーまたは親フォルダーにある babel.licenses ファイルを読み込むため、開発者はパイプラインの構成なしで作業を続けられます。パッケージのセットアップを参照してください。
パイプライン
trigger:
- main
pool:
vmImage: ubuntu-latest
variables:
buildConfiguration: Release
steps:
- task: UseDotNet@2
displayName: Install the .NET SDK
inputs:
version: 10.0.x
- task: NuGetAuthenticate@1
displayName: Authenticate to the Babel feed
- script: dotnet restore DevOpsIntegration.sln
displayName: Restore
- script: dotnet build DevOpsIntegration.sln --configuration $(buildConfiguration) --no-restore
displayName: Build and obfuscate
env:
BABEL_LICENSE: $(BabelLicense)
- task: PublishPipelineArtifact@1
displayName: Publish the obfuscated output
inputs:
targetPath: DevOpsIntegration/bin/$(buildConfiguration)/net10.0
artifact: DevOpsIntegration注目すべき点が 2 つあります。
プライベートフィードにアクセスできるようにしているのは NuGetAuthenticate@1 です。このタスクはパイプライン自身の ID で資格情報プロバイダーを構成するため、上の NuGet.config には packageSourceCredentials セクションも、自分のトークンも必要ありません。
シークレット変数は、スクリプトに自動的には渡されません。シークレットとして指定する意味はそこにあります。ビルドステップの env: ブロックが BabelLicense をプロセスにマッピングしており、キーが現れるのはこの場所だけです。
難読化は dotnet build の一部として実行され、Babel 用の独立したステップはありません。Babel のログはビルド出力に表示され、ライセンスまたは構成のエラーがあるとステップは失敗します。
ビルドエージェントのイメージに必要なのは、.NET SDK を実行できることだけです。ubuntu-latest は最も安価な選択肢で、どのターゲットランタイムにも使えます。Babel は IL を処理するため、Linux のビルドエージェントでも Windows 向けのアセンブリを難読化できます。.NET Framework のターゲット、WPF、Windows Forms のように、ビルドそのものに Windows が必要な場合は windows-latest を使用してください。
難読化を設定する
Babel は、プロジェクトファイルの MSBuild プロパティで設定します。よくある構成は、ローカルでのデバッグが通常どおり動作するように Debug ビルドには手を加えず、Release ビルドを保護するものです。
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<BabelEnabled>false</BabelEnabled>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<StringEncryption>stream</StringEncryption>
<ControlFlowObfuscation>if=on;goto=on;switch=on;case=on;call=on</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
<ResourceEncryption>true</ResourceEncryption>
</PropertyGroup>すべてのプロパティはパッケージリファレンスに一覧があり、それらが Babel の機能にどう対応するかは難読化の設定で説明しています。
アセンブリ全体に対する切り替えよりも細かく制御するには、ルールファイルを追加します。次のルールは、短いメソッドには制御フロー難読化を適用しないようにします。短いメソッドでは、フラット化のコストが隠蔽の効果を上回るためです。
<?xml version="1.0" encoding="utf-8" ?>
<Rules>
<Rule name="reduce control flow" feature="control flow" exclude="false" applyToMembers="true">
<Target>Classes,Structures</Target>
<Pattern>*</Pattern>
<Properties>
<MaxSwitchTargets>5</MaxSwitchTargets>
<MinInstructionCount>18</MinInstructionCount>
<UseValueEncryption>false</UseValueEncryption>
</Properties>
<Description>Do not scramble methods with few instructions.</Description>
</Rule>
</Rules>BabelRules プロパティで、ビルドにこのファイルを指定します。
<PropertyGroup>
<BabelRules>$(MSBuildThisFileDirectory)babelRules.xml</BabelRules>
</PropertyGroup>結果の確認
パイプラインの成果物をダウンロードし、アセンブリが難読化されているものと思い込まずに、実際に難読化されていることを確認してください。Babel による難読化の検出のサンプルは、これを自動的にテストする方法を示しています。このテストはゲートとしてパイプラインに追加する価値があります。そうしないと、ライセンスの設定ミスやビルドステップのスキップがあっても、何も失敗しないまま難読化されていないアセンブリが配布されてしまいます。
本番環境から届くスタックトレースをデコードする予定がある場合は、ビルドで生成されたマップファイルを保管しておいてください。スタックトレースのデコードを参照してください。