u-ryo's blog

various information for coding...

Category: Nginx

Spring Auth and JWT Behind the Reverse Proxy

| Comments

JHipsterのSpring Authなapplicationを httpsのreverse proxy(nginx)の後ろに置いて、 GoogleのOAuth2でJWTな認証をしようとしました。 当然、backend serverからはGoogle APIに自分のhost名でaccessするような URLを返してしまい、Google APIから戻ってきたところでJWT認証は弾かれます。 backend serverはfrontend serverの名前を知らないんですから、 そりゃあ当然です。 こういうreverse proxyの後ろにbackend server置いてOAuth2 + JWTなんて そもそもダメなの? 何とかならないの? と調べてみると、 Spring Boot and OAuth2: redirect url over reverse proxyに、 reverse proxy側でX-Forwarded-Portとかのproxy用HTTP Response Headerを設定し、 Spring application側でserver.use-forward-headers=trueにすればいいよ、 とあったので、 じゃぁnginxではどうやるのだろうと調べると、 Nginx のリバースプロキシ設定のメモにありました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
  listen 80;
  server_name hoge.com;

  location / {
    proxy_set_header X-Real-IP $remote_addr;
    index index.html index.htm;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://127.0.0.1:3000;
  }
}

この通りやってみると、 何故かGoogle APIにはhttp://proxy-server/...で渡っており、 じゃぁっていうんでproxy_set_header X-Forwarded-Proto https; とベタ書きしてみてもダメで、 うーんとか思っていると、 nginx でリバースプロキシするときの Tipsoffじゃなくてproxy_redirect http:// https://;という記述があったので、 試してみると、上手く行きました。 あーちなみに、proxy_set_header X-Forwarded-Proto https;も ベタ書きじゃないとダメでした。 結局うちの場合は、以下の通りになりました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # Everything is a 404
        location / {
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-Proto https;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_redirect http:// https://;
                proxy_pass http://walt.mydns.bz:10022/;
        }

        # You may need this to prevent return 404 recursion.
        location = /404.html {
                internal;
        }
}

で、sudo /usr/sbin/nginx -s reloadです。

でも、これでGoogle APIから無事戻ってくるようにはなったものの、 その後「No-providerで登録」になってしまい、 まだ完成しません。 ただ、その問題は別のもののようで、一歩は進んだと思うので、記事にしました。

↑その「No-providerで登録」になってしまうのは、 backendで以下のようなerrorが出ていて。

1
2
3
4
5
6
7
8
9
10
11
javax.validation.ConstraintViolationException: Validation failed for classes [bz.mydns.walt.canmatch.domain.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
        ConstraintViolationImpl{interpolatedMessage='must match "^[_'.@A-Za-z0-9-]*$"', propertyPath=login, rootBeanClass=class bz.mydns.walt.canmatch.domain.User, messageTemplate='{javax.validation.constraints.Pattern.message}'}
]
        at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140)
        at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80)
        at org.hibernate.action.internal.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:197)
        at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:75)
        at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:626)
   :
   :

何なんでしょうね。 これは、account mail addressが「w.disney@somecompany.co.jp」みたいな 「.」が入るものなんですが、それがいけないとかなのでしょうか。 というのも、フツーの「gepetto@gmail.com」みたいなmail accountなら 全く同じcodeで何の問題もなく入れるのです。 「must match」の対象が何なのか、よく分かりません。

Arukas Cloud

| Comments

Sakuraがやっているからそのreverse spellingだというArukas、 docker hostingをやっていて、 credit card登録は強制されますが1 containerなら無料というので、 専用SSH machine作ってJHipsterの開発/buildに使おうと企みました。

無料枠ではDocker Hubにあるものしか使えないとはいえ、 SSH serverの入ったdocker imageなんて色々あります。 でも使ってみると、基本的にはrootでlogin、 当然root loginをpermitしていて、 securityを維持するにはrootのpasswordを変える、 userを作って.ssh/authorized_keysを作る、 とかなわけですが、けどroot loginのpermissionを切って sshdをrestartすると、docker終わっちゃうんですよね当たり前ですが。 何かもっとこう、最初からUser作ってrootは禁止して、 とかっていうの無いかなー、 と物色していると、どれだったか忘れましたがgithub.comから 自分が登録したkeyをADDしてるものがあって、 あーなるほどー、と思って。 ADD https://github.com/$GITHUB_USER.keys /home/$GITHUB_USER/.ssh/authorized_keysということですね。

で、結局どうしても自分の欲しいものがなかったので、 Dockerfile自作することにしました。 元になるimageはDocker Hub上になければいけないので、 Docker Hubを使ってGitHubにあるDockerfileからimageを自動生成するを参考に、 Github → Docker Hub → Arukasという流れになるように、 自分のGithub repository自分のDocker Hub repository自分のArukas App と繋げて、 回るようにします。

Arukasでは変数定義が出来るというのでDockerfileでGithub user名をENV定義して、 でもそうするとDocker HubではbuildにコケるのでENVには何かdefault値がないとダメで、 でもdefault値を付けちゃうとそのdefault値がArukas Appまで残っちゃうんですよね。 Docker コンテナの動作に必要な設定を起動時に渡すなどを見て、 え、shell script内じゃないとダメ? じゃぁっていうんでentrypoint.sh作って試してみたんですけど、 Arukasで起動に失敗しました。Docker Hubではbuild通ったのに。 Arukasでは起動時のmessageとかは出ないので、何が悪いのか分かりません。 困り果ててArukasのchatで相談投げると、翌営業日にはすぐreplyをくれて、 動作確認のためということでpull requestまで作ってくれました。 まずは早速そのまま取り込んで起動してみたんですが、 やはり起動に失敗。 けれども、別のaccountの方でENVを編集することで再起動させてみたところ うまく行ったので、何だったんでしょう? 結局今はどちらでも上手く立ち上がるようになりました。 ありがとうございました。

他、Arukasでハマったのは、開放するPort、 後で色々service立ち上げようと思って3000番とか8080番とか色々書いておいたら、 Endpointが有効にならないんですね。 これもArukasのchatで聞いて教えてもらったんですが、 確かによく読むと、「Docker image立ち上がった時点で 書いたPortが全て開いてないとEndpointが有効にならない」 と書いてありますね。 「一番上に書いたPortだけEndpointに繋がる」というのは 「アプリ編集」画面のEndpointのinfoに書いてあるからわかったんですが。

baseとなるdistributionは、 軽いというAlpine Linuxに。 アプリの管理もapk add ...コマンドで出来ますし、 packageもscreenやらopenjdk8やらgradleやら 結構色々あってびっくりです。 何かubuntuじゃなくてalpineで十分な気がしてきました。

目的としていたjhipster applicationのbuildは失敗しました。 specがしょぼい(128MB memory)のか通信量に制限があるのか、 jhipsterコマンドの途中で止まり、 applicationのscaffoldingが出来ません。 まー、無料枠なのでmachine specについては文句言えませんから、 しょーがないのかなーと。

ただ、Endpointがhttpsで手に入るので、 Nginxによるリバースプロキシの設定方法を参考に、 nginxによるreverse proxyとして使おうかなー、 と思っています。 custom domainも使えるとのことですが、 CDNと同じくDNSを自分でcontrol出来ねばならないので、 dynamic DNSでは使えず、custom domainは諦めました。

  • docker hubの成果物(といってもpull requestくれた山田さんのおかげモノですが。山田さんありがとうございます!)

Githubにkeyを登録してあれば、Arukas AppでImageにこれ↑を指定し、 ENVGITHUB_USERをGithubのuser名、 PROXY_PASSをreverse proxyしたいURL、 にして立ち上げると、 当該hostにssh出来、 またEndpointで指定したURLでreverse proxy出来ます。

1
2
ln -sf /dev/stdout /var/log/nginx/access.log
ln -sf /dev/stderr /var/log/nginx/error.log

こんな技があるんですねーなるほどー。