CSEncrypt コマンドでパスワードの暗号化

Windows Azure SDK 1.5でリモートデスクトップ接続時に使用するパスワードやらを暗号化するのに使える、CSEncryptコマンドラインツールが追加されました。

Azure SDK 1.5が出る以前はどうだったかというと…

にある通り、パスワード文字列をバイト配列にして証明書読み込んで読み込んだ証明書を使って暗号化して、できあがったものをBase64でエンコードするという超絶めんどい作業をする必要がありました。

[Reflection.Assembly]::LoadWithPartialName("System.Security")
$pass = [Text.Encoding]::UTF8.GetBytes("<Password>")
$content = new-object Security.Cryptography.Pkcs.ContentInfo ?argumentList (,$pass)
$env = new-object Security.Cryptography.Pkcs.EnvelopedCms $content
$env.Encrypt((new-object System.Security.Cryptography.Pkcs.CmsRecipient(gi cert:\CurrentUser\My\<Thumbprint>)))
[Convert]::ToBase64String($env.Encode())

こんな感じですね。

さてこれがAzure SDK 1.5に付属するCSEncryptコマンドだとどうなるかというと。

csencrypt Get-PasswordEncryptionCertificate
コマンドを実行して証明書一覧を表示して拇印をコピーして…

csencrypt Encrypt-Password -CopyToClipboard -Thumbprint "<さっきの拇印>"
コマンドを実行してパスワードを入力するだけ!

あとは出力された文字列を(改行除いて)コピーすればOK!ほら簡単でしょ?(

ちなみに -CopyToClipboard オプションを付けておけば勝手に結果をクリップボードにコピーしてくれます。
※Outputオプションでファイルに出力することもできます。(やっぱり改行はされてますけど)
そのほかにもNew-PasswordEncryptionCertificateオプションで証明書の生成もできるのでお手軽ですね。

ちなみにパスワードを書いたテキストファイルを標準入力として読み込ませれば手打ちしなくてもイイです。

オプションや内容についてはこちらを参照。

というわけでコマンドでやるにも便利な感じになりましたとさ。

Windows Developer Preview をVHDブートさせる

Windows Developer PreviewをVHDブートさせる方法はいろいろありますが、基となるVHDをWindows Server 2008 R2のHyper-V 2.0とかで作ってしまうとそのままじゃブートしません。

※ブートメニューでWindows Developer Previewを選ぶと 0xC0000248 な署名が検証できない云々で⊂ミ⊃^ω^)アウアウ

解決方法は一応ありますが自己責任で、ということで載せておきます。
簡単にいうと、ブートローダ(Winload)がWindows 7のままだとエラーになるわけで、Windows Developer Previewのブートローダ(Winload)になるように修復してあげればいいだけです。

  1. Hyper-V 2.0上でWindows Developer Previewをインストール(※ディスクサイズはVHDファイルを置くWindows 7等の空きディスク容量より少なくなるようにしましょう)
  2. VHDファイルをコピーして Windows OSをVHDブートさせる2つの方法 (@IT) あたりを参考にBCDを弄ります(このままだと上記のエラーになる)
  3. Visual BCD Editor for Windows 7/Vista をダウンロードしてインストールします
  4. ディスクの管理でコピーしたVHDをマウントします
  5. Visual BCD Editorを立ち上げ、「Repair」-「Repair BCD」メニューを選択します
    image
  6. Bcd DriveはSystemのままでWin7Folderを先ほどマウントしたVHDのWindowsフォルダ(Windows Developer PreviewのWindowsフォルダ)を選択して修復します。

以上でブートローダがWindows Developer PreviewのものにかわってちゃんとVHDブートできるようになると思います。

WP_000003

まーWindows Developer PreviewのISOをブートできたらこんなことせずに修復できると思うのですが手元にそういったものはなかったのでこんな方法になりました。

Windows Azure AppFabric ACS でカスタムのサインイン画面を作る

Windows Azure AppFabric ACS使ってフェデレーション認証するのはいいけど、味気ないサインイン画面で辟易してる方も多いんじゃないでしょうか。

↓味気ないサインイン画面

というわけで今日はこいつのカスタマイズをしたいと思います。

ゴールとして今回はASP.NET MVC3のログイン画面内で上記のサインイン画面(というかセレクタ画面)を埋め込んでアプリケーションでちゃんと(?)制御できるようにしたいと思います。

続きを読む

ASP.NET Univarsal Providers のセッションプロバイダを使ってみる (4)

性懲りもなく4回目。

@kazuk さんが「無駄な処理をしない事と、重い処理をバックグラウンドにする事」ということで、
削除処理を毎回するんじゃなくてある程度間隔あけて+別スレッドでやるべきだろJKとおっしゃられました。
というわけで @takepara さんのHighPerformanceSessionStateProviderに追加してみました。

↓結果

少しパフォーマンスよくなりました。ただMAXも2秒とかなので、平均して落ち着いた感。エラーもなくなってますね。
クライアントは↓

多少ばらつきありますがネットワーク的に遠いところ(日本)なのでまぁこんな感じですかね。ゴミセッションの削除がはしったと思われるタイミングでもそんなに重くなってないのが特徴です。

とまぁこんな感じでかなりまともに使えるセッションプロバイダになった気がします。
ロールやメンバシップの部分はASP.NET Univarsal Providersのが良しなに利用できると思うので組み合わせるといいんではないでしょうか。

最終的なソースはこちら↓

// create: 2011.08.23
// author: @takepara (http://tinyurl.com/3kebwlu) , @kazuk (http://tinyurl.com/3r5vjkq)

using System;
using System.Configuration;
using System.Data.Common;
using System.Reflection;
using System.Web;
using System.Web.Providers;
using System.Web.Providers.Entities;
using System.Threading;

namespace UniversalProviders
{
	public class HighPerformanceSessionStateProvider : DefaultSessionStateProvider
	{
		private string _connectionStringName;
		private bool _initialized = false;
		private DateTime _lastSessionExpireInvoked = DateTime.MinValue;
		private object _lockSessionExpire = new object();
		private bool _working = false; 

		public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
		{
			_connectionStringName = config["connectionStringName"];

			base.Initialize(name, config);
		}

		private void ExecuteSql(ConnectionStringSettings connectionStringSettings, Action<DbProviderFactory, DbCommand> functor)
		{
			var providerName = connectionStringSettings.ProviderName;
			var factory = DbProviderFactories.GetFactory(providerName);
			using (var connection = factory.CreateConnection())
			{
				connection.ConnectionString = connectionStringSettings.ConnectionString;
				connection.Open();

				var command = connection.CreateCommand();

				functor(factory, command);
				command.ExecuteNonQuery();
				connection.Close();
			}
		}

		private void CreateSessionIndex(ConnectionStringSettings connectionStringSettings)
		{
			ExecuteSql(connectionStringSettings, (factory, command) =>
			{
				command.CommandText = @"IF not EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[Sessions]') AND name = N'IX_Sessions')
begin
CREATE NONCLUSTERED INDEX [IX_Sessions] ON [dbo].[Sessions] 
(
	[Expires] ASC
)
end
";
			});
		}

		private void RemoveExpiredSessions( 
			ConnectionStringSettings connectionStringSettings) 
		{ 
			if( _working ) return; // やってる人が居るならやらんでいいでしょ 
			_working = true; 
			try { 
				ThreadPool.QueueUserWorkItem( (object state)=> 
				{  
					try 
					{ 
						ExecuteSql(connectionStringSettings, (factory, command) => 
						{  
							command.CommandText = "delete Sessions where Expires < @0"; 
							var parameter = factory.CreateParameter(); 
							parameter.ParameterName = "@0"; 
							parameter.Value = DateTime.UtcNow; 
							command.Parameters.Add(parameter); 
						}); 
					} 
					finally { _working = false; } 
				}); 
			} catch { _working = false; throw; } 
		}

		private MethodInfo GetCreateSessionEntities()
		{
			var modelHelper =
				Assembly.GetAssembly(typeof(Session)).GetType("System.Web.Providers.Entities.ModelHelper");
			return modelHelper.GetMethod("CreateSessionEntities", 
				BindingFlags.NonPublic | BindingFlags.Static);
		}

		public override void InitializeRequest(HttpContext context)
		{
			var connectionStringSettings = ConfigurationManager.ConnectionStrings[_connectionStringName];
			if (!_initialized)
			{
				var initializer = GetCreateSessionEntities();
				initializer.Invoke(null, new object[] { connectionStringSettings });
				CreateSessionIndex(connectionStringSettings);
				_initialized = true;
			}

			if( _lastSessionExpireInvoked < DateTime.UtcNow - TimeSpan.FromSeconds(30) ) 
			{ 
				if( Monitor.TryEnter( _lockSessionExpire ) ) { 
					// ロックに入れたスレッドでだけ処理する、入れなかったスレッドでは処理しない 
					DateTime old = _lastSessionExpireInvoked;
					_lastSessionExpireInvoked = DateTime.UtcNow; 
					try 
					{ 
						try 
						{ 
							RemoveExpiredSessions(connectionStringSettings); 
						} 
						catch 
						{ 
							// 失敗した時はタイマ値を戻すことで別のスレッドにやってもらう 
							_lastSessionExpireInvoked = old; 
							throw; 
						} 
					} 
					finally 
					{ 
						// 絶対にMonitorを出る 
						Monitor.Exit( _lockSessionExpire ); 
					} 
			   } 
			}
		}
	}
}

Windows Azure AppFabric Caching を使ってみる

ASP.NET Universal ProvidersおよびカスタムのHighPerformanceSessionStateProviderを見てきたわけですが、本命(?)のWindows Azure AppFabric Cachingだと、どんなもんなんでしょうか。

構成等は今までと同じで、Windows Azure AppFabric Cachingも同じエリア(東アジア)に作って試してみました。

※Azure AppFabric Cachingの使い方等は適当に検索で(
※アセンブリ追加してWeb.configにもろもろ追加+セッションプロバイダの変更をしただけです。(ちなみに今回はSSLなエンドポイントを使用)

↓結果

えらい早い ( ゚д゚) 12万セッションだだだっと作るのに3分ぐらい。

作りながら同じセッションで継続アクセスした場合はこんな感じ

削除処理とかはAzure AppFabric Caching側が面倒みるのでいつかわかりませんが、きちんと破棄されたりしているようです。

とまぁこんな感じでした。

ASP.NET Univarsal Providers のセッションプロバイダを使ってみる (3)

第1回第2回に続いて第3回です。

前回はそのまんまASP.NET MVC3ロールに含まれてるASP.NET Univarsal Providersを使って軽く負荷テストしたりしてみました。
結果は想定通りだったわけですが、なんと、たけぱらさんにこんなコメントをつけて頂き、しかもHighPerformanceSessionStateProvider という実装までしてくださいました。

こりゃー再度テストしてみないといけないな!ということで。

条件は前回と同じくAzure上にSサイズで5インスタンスほど、たけぱらさん作HighPerformanceSessionStateProviderを使ったMVC3ロールを展開。クライアントも同様にSサイズなWorkerロールを20インスタンスほど作って並行で6000セッションづつ作りまくる感じにしました。

HighPerformanceSessionStateProvider はたけぱらさんのBlogからコピペ。ただSQL Azureで利用する際に1点だけ修正が必要なので注意です。

CreateSessionIndex メソッド内

		private void CreateSessionIndex(ConnectionStringSettings connectionStringSettings)
		{
			ExecuteSql(connectionStringSettings, (factory, command) =>
			{
				command.CommandText = @"IF not EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[Sessions]') AND name = N'IX_Sessions')
begin
CREATE NONCLUSTERED INDEX [IX_Sessions] ON [dbo].[Sessions] 
(
	[Expires] ASC
)
end
";
			});
		}

SQL Azure上で利用できないT-SQLの構文があるので、上記10行目に書かれてた「ON [PRIMARY]」を削除します。

Web.configも書き換えたら準備完了です。

で、実際に見てみると…だいぶ早い感じ。セッションタイムアウトは10分にしてたのですが、6000セッションx20作成中に生存してるセッションが2万弱とかだったのに対し4万弱ほど作れてたりします。

ログみると

タイムアウトになったりなNGが5000ちょいほどありますが…概ね良好な感じですね。
※タイムアウトなるのはコードの問題な気がしないでもない…例外まで補足してなかったので詳細不明(ひょっとしたらSQL Azure側?うーん)

で、前回まともに動作しなかった同じセッションIDで更新したり、ごみセッションを削除する処理ですが

こんな感じでゴリゴリセッション作成してる間でも500msとかでアクセスできてますね。(=セッションテーブルの更新もOK)

また最後の15分経過後のアクセスも5秒かかっていますが問題なくできてます。
ちなみにこの時のごみセッション数は32000弱。

ちょっとしたひと手間でまともに使える感じがします!

HighPerformanceSessionStateProvider イイネ!

ASP.NET Univarsal Providers のセッションプロバイダを使ってみる

ASP.NET Univarsal Providersのセッションプロバイダって、セッションの破棄もしてくれるの?という話題から。

結論から言うと、「期限切れのセッションは削除してくれます。但しパフォーマンスに問題がありそうなやり方で。」となります。

@kazuk さんにさらっと見てもらえたので先に結論がわかりました。ありがとうございます。

さて実際にその様を見てみましょうか。

続きを読む

ASP.NET Universal Providers

さっきの投稿でも書いた System.Web.Providers ですが、名称としては ASP.NET Universal Providers というそうです。

簡単に何をするものか説明すると、「ASP.NET におけるセッションステート、メンバシップ、ロール、プロファイル機能を、SQL Compact と SQL Azure にも対応させるツール」 という感じでしょうか。パクリですけど。

Node.js、Ruby、Python を Windows Azure で利用する Smarx Role ~ クラウドカバー Episode 48 より引用

ASP.NET におけるセッションステート、メンバシップ、ロール、プロファイル機能を、SQL Compact と SQL Azure にも対応させるツール “ASP.NET Universal Provider” がアルファ版としてリリースされました。

これにより、これまでのオンプレミスな Windows & SQL Server という組み合わせに加え、低トラフィックな Web サイトでは共有ホスティング & SQL Compact を利用、トラフィックが多い Web サイトでは Windows Azure & SQL Azure を利用する、といったことが可能になります。

ということで便利でナイスなプロバイダなわけですが、現時点でnugetから入手できるASP.NET Universal Providerは 0.1です。

でも、ASP.NET MVC 3 Webロールテンプレートに含まれる System.Web.Providers は1.0扱い?なんですよね。アセンブリも違うもののようです。(中身まで見たわけじゃないですがバイナリは異なる)

そのうちNugetにもあがる気はしますが、とりあえず正式版?がASP.NET MVC 3 Webロールテンプレートには含まれてるようです。

ちなみにアセンブリは<プロジェクトフォルダ>\packages\System.Web.Providers.1.0 のLibにあります。EULAやReadme.htmlもあるので使い方とか参考にどうぞ。

image

これはちゃんと使いこなしたいですね!

※EULAのどこどう見たらGoLiveなのか詳しい人に教えてもらいたいところ…ちゃんと読めという話ですが。

※EULAの補足。nugetで入手できる0.1はALPHA版ということで、商用利用が制限されています。ただ、ASP.NET MVC 3 Webロールに含まれてる1.0のほうはこの制限がないので、商用利用OK、Go-liveとみていいと思います。

image

 

いろいろ盛り込んでくれますね!Azure Team!

Windows Azure ASP.NET MVC 3 Web Role で使っているプロバイダ

先日の投稿でも書きましたが、Windows Azure Tools for Visual studio v1.4 (August 2011 Update)でASP.NET MVC 3のWebロールテンプレートが追加されました。

で、このASP.NET MVC 3なWebロールを使うと今まで面倒だったアセンブリの追加とかが不要なわけですが、そのまま何も気にせずデプロイするとエラーになります。

image

よくみる(?)画面ですね。

さて、この原因ですが Deploying the Windows Azure ASP.NET MVC 3 Web Role にもある通り、ASP.NET MVC 3 Webロールプロジェクトテンプレートに含まれてるWeb.configにて、SQL Expressなカスタムセッションを利用するようになっているのが原因です。

解決方法はWade Wegner氏が書いてるとおりSQL Azure使うように正しく接続文字列変えてあげると良いのですが、面倒な場合はsessionState属性のmodeをOffとかInProcにすればいいんじゃないでしょうか。(投げやり)

※というのもちゃんとアプリケーション作るときはこの辺きちんと考慮するだろうし、デフォルトのままデプロイしたりするのは訓練されたあじゅらーぐらいな気もするので…

まぁ、そのうち修正されるかもしれませんがちょっとしたbad know-howということで…

– 重要な補足 –

たけはらさんにTumblrで教えていただきました。

どうもこのASP.NET MVC 3 Webロールテンプレートで使っているセッションやメンバシッププロバイダ等各種プロバイダはEntityFrameworkベースのSystem.Web.Providersだそうです。

Introducing System.Web.Providers – ASP.NET Universal Providers for Session, Membership, Roles and User Profile on SQL Compact and SQL Azure – Scott Hanselman

未来を先取りテンプレート!!

ということは、Bad know-howで問題点として片付けるんじゃなくて、新しいテクノロジを活用しよう!っていう方向に持っていくべきですかね。

たけはらさんありがとうございます。

Windows Azure Tools for Microsoft Visual Studio 2010 August 2011

ひさびさの開発者向け大型アップデート来ました。

Windows Azure Tools for Microsoft Visual Studio 2010 August 2011 です!

今までTools for VSは1.3ベースだったんですが、これで1.4ベースとなりました。

続きを読む