--- title: Certified OpenID Connect ProviderのUAAをCloud Foundryにデプロイする方法とSpring Bootとの連携方法 tags: ["Cloud Foundry", "Java", "UAA", "Spring Boot", "OpenID Connect"] categories: ["Dev", "PaaS", "CloudFoundry", "UAA"] date: 2018-09-10T17:37:42Z updated: 2018-10-18T02:07:29Z --- [Certified OpenID Connect Provider](https://openid.net/certification/)の一つである[UAA](https://github.com/cloudfoundry/uaa)([ドキュメント](https://docs.cloudfoundry.org/uaa/))をCloud Foundryにデプロイします。 UAAはCloud Foundryのユーザー認証・認可にも使われているコンポーネントでCFユーザーにはお馴染みですが、ただのJavaアプリなので普通にデプロイして、自システムで使用するスタンドアローンなOIDC Providerとして利用可能です。 古くから利用されておりBattle Testedなコンポーネントな一方、Spring Boot登場前より存在するため、普通のSpringアプリケーションでありwar形式でのデプロイとなります。 **目次** ### UAAのデプロイ UAAのGithubレポジトリではソースしかダウンロードできないのですが、Stark And Wayne社が[warにパッケージング](https://github.com/starkandwayne/uaa-war-releases/releases)したものを配布しているので、こちらを使用します。 ファイル名を`ROOT.war`にしてダウンロードしてください。 ``` wget https://github.com/starkandwayne/uaa-war-releases/releases/download/v4.21.0/cloudfoundry-identity-uaa-4.21.0.war -O ROOT.war ``` 次にUAAの設定をカスタマイズするためのベースのYAML(`uaa.yml`)を用意します。 ```yaml cat < uaa.yml issuer: uri: https://((route)) uaa: url: https://((route)) encryption: encryption_keys: - label: uaa-encryption-key-1 passphrase: ((uaa_encryption_key_1)) active_key_label: uaa-encryption-key-1 scim: users: - admin|((admin_user_password))|admin||||uaa userids_enabled: true user: override: true require_https: true oauth: authorize: ssl: true clients: uaa_admin: override: true authorized-grant-types: client_credentials scope: "" authorities: clients.read,clients.write,clients.secret,uaa.admin,scim.read,scim.write,password.write secret: ((admin_client_secret)) user: authorities: - openid - scim.me - password.write - uaa.user - uaa.offline_token jwt: token: queryString: enabled: true revocable: true policy: accessTokenValiditySeconds: 43200 refreshTokenValiditySeconds: 2592000 global: accessTokenValiditySeconds: 43200 refreshTokenValiditySeconds: 2592000 activeKeyId: uaa-jwt-key-1 keys: uaa-jwt-key-1: verification-key: ((uaa_jwt_signing_key.public_key)) signingKey: ((uaa_jwt_signing_key.private_key)) refresh: restrict_grant: false unique: false format: jwt login: url: https://((route)) selfServiceLinksEnabled: true serviceProviderKey: ((uaa_service_provider_ssl.private_key)) serviceProviderKeyPassword: "" # TODO: Remove this when UAA defaults this value serviceProviderCertificate: ((uaa_service_provider_ssl.certificate)) zones: internal: hostnames: - ((route)) variables: - name: admin_user_password type: password - name: admin_client_secret type: password - name: uaa_jwt_signing_key type: rsa - name: uaa_encryption_key_1 type: password - name: default_ca type: certificate options: is_ca: true common_name: ca - name: uaa_service_provider_ssl type: certificate options: ca: default_ca common_name: ((route)) alternative_names: [((route))] EOF ``` YAMLのカスタマイズ性を高めるのと、Credentialを自動生成するために[BOSH CLI](https://bosh.io/docs/cli-v2/)を使用します。 BOSH CLIは実行可能バイナリとして配布されているのでダウンロードして`PATH`に追加してください。 次にUAAのOAuth2クライアントを追加するための設定をBOSH CLIの[ops file](https://bosh.io/docs/cli-ops-files/)形式で定義します。 ここでは[http://localhost:8080](http://localhost:8080)のアプリを想定して`demo_dev`というクライアントを作成するための設定を作ります。 ```yaml mkdir -p ops-files cat < ops-files/add-demo.yml - type: replace path: /oauth/clients/demo_dev? value: name: Demo Dev authorities: uaa.none authorized-grant-types: authorization_code,refresh_token,password override: true redirect-uri: http://localhost:8080/login/oauth2/code/uaa scope: openid,role secret: ((demo_dev_client_secret)) - type: replace path: /variables/name=demo_dev_client_secret? value: name: demo_dev_client_secret type: password EOF ``` 次のSMTP通知設定を行うためのops fileを作成します。 ```yaml cat < ops-files/smtp.yml - type: replace path: /smtp? value: host: ((smtp_host)) port: ((smtp_port)) user: ((smtp_user)) password: ((smtp_password)) starttls: true EOF ``` `uaa.yml`にops fileを合成した上で`ROOT.war`に埋め込むためのscriptを作成します。ここでは次に作成する`.profile`ファイルも埋め込みます。 デプロイ後のホスト名は`demo-uaa.cfapps.io`とします。 ```bash cat <<'EOF' > embbed-manifest.sh #!/bin/bash set -e bosh int uaa.yml \ -o ops-files/smtp.yml \ -o ops-files/add-demo.yml \ -v route=demo-uaa.cfapps.io \ -v smtp_host=smtp.gmail.com \ -v smtp_port=587 \ --vars-store=credentials.yml \ > WEB-INF/classes/uaa.yml jar -uvf ROOT.war .profile WEB-INF EOF chmod +x embbed-manifest.sh ``` UAAのデータベース接続情報(MySQL/PostgreSQL/SQL Server)を環境変数に設定するために[Pre-Runtime Hooks](https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#profile)を使用します。 ここでは`demo-uaa-db`という名前のMySQLサービスインスタンスをバインドする前提とします。環境変数`VCAP_SERVICES`に含まれる`demo-uaa-db`サービスインスタンスに関する接続情報を`jq`コマンドで取得するためのスクリプトを`.profie`ファイルに記述します。 ```bash cat <<'EOF' > .profile CREDS=$(echo $VCAP_SERVICES | jq -r ".[] | map(select(.name == \"demo-uaa-db\"))[0].credentials") export DATABASE_HOSTNAME=$(echo $CREDS | jq -r .hostname) export DATABASE_PORT=$(echo $CREDS | jq -r .port) export DATABASE_USERNAME=$(echo $CREDS | jq -r .username) export DATABASE_PASSWORD=$(echo $CREDS | jq -r .password) export DATABASE_NAME=$(echo $CREDS | jq -r .name) export DATABASE_URL=jdbc:mysql://${DATABASE_HOSTNAME}:${DATABASE_PORT}/${DATABASE_NAME} EOF ``` SMTPに接続情報を`credentials.yml`に定義します。 ```yaml cat < credentials.yml smtp_user: your-account@gmail.com smpt_password: your-password EOF ``` `embbed-manifest.sh`を実行してデプロイ可能な`ROOT.war`を作成します。 ``` mkdir -p WEB-INF/classes ./embbed-manifest.sh ``` Cloud Foundryの`maniefst.yml`を作成します。 ```yaml cat < manifest.yml applications: - name: demo-uaa memory: 1g instances: 1 path: ROOT.war health-check-type: http health-check-http-endpoint: /healthz services: - demo-uaa-db env: SPRING_PROFILES: mysql DATABASE_DRIVERCLASSNAME: org.mariadb.jdbc.Driver DATABASE_MAXACTIVE: 4 DATABASE_MAXIDLE: 3 DATABASE_MINIDLE: 1 EOF ``` `cf push`でアプリをデプロイします。ここではPivotal Web Servicesを使用し、MySQLサービスとしてClearDBサービスの`sprk`プランを利用します。 ``` cf create-service cleardb spark demo-uaa-db cf push ``` ![image](https://user-images.githubusercontent.com/106908/45314584-18fb8680-b56d-11e8-94f2-a885f38740f0.png) できました。 `admin`ユーザーのパスワードは`bosh int credentials.yml --path /admin_user_password`で表示されます。 ### CLIコマンドを使ってUAAにアクセス UAAのCLIは[Ruby版](https://github.com/cloudfoundry/cf-uaac)とExperimentalな[Go版](https://github.com/cloudfoundry-incubator/uaa-cli)があります。 #### Ruby版 ``` gem install cf-uaac ``` 使い方は[こちら](https://docs.cloudfoundry.org/uaa/uaa-user-management.html) 接続先UAAの設定 ``` uaac target https://demo-uaa.cfapps.io ``` `uaa_admin`クライアントのClient Credentials Grant Typeでアクセストークンを取得してみます。 ``` ADMIN_SECRET=$(bosh int --path /admin_client_secret credentials.yml) uaac token client get uaa_admin -s ${ADMIN_SECRET} ``` クライアント一覧表示 ``` uaac clients ``` 出力結果 ```yaml demo_dev scope: openid role resource_ids: none authorized_grant_types: authorization_code refresh_token password redirect_uri: http://localhost:8080/login/oauth2/code/uaa autoapprove: authorities: uaa.none name: Demo Dev lastmodified: 1536702358000 uaa_admin scope: uaa.none resource_ids: none authorized_grant_types: client_credentials autoapprove: authorities: clients.read password.write clients.secret clients.write uaa.admin scim.write scim.read lastmodified: 1536702358000 ``` ユーザー一覧表示 ```` uaac users ```` 出力結果 ```yaml resources: - id: 925083e7-81cd-4d87-8751-cb631459f390 meta version: 4 created: 2018-09-10T17:44:26.000Z lastmodified: 2018-09-11T21:45:54.000Z name familyname: givenname: emails: - value: admin primary: false groups: - value: 3764195c-aa98-4d59-9a54-77345f9fbf67 display: openid type: DIRECT - value: 8b256da8-4027-42ee-bd0f-76453c17c93e display: uaa.user type: DIRECT - value: 8bf4e1fa-28e7-430a-b0ed-bdadbcd1f120 display: uaa.offline_token type: DIRECT - value: ed19972a-7abc-4c7b-8681-1e7b6ef25acf display: scim.me type: DIRECT - value: 7ae25c7c-b96a-40a1-9273-ccdeca98ac04 display: password.write type: DIRECT approvals: active: true verified: true origin: uaa schemas: urn:scim:schemas:core:1.0 username: admin zoneid: uaa passwordlastmodified: 2018-09-10T17:44:26.000Z schemas: urn:scim:schemas:core:1.0 startindex: 1 itemsperpage: 100 totalresults: 1 ``` #### Go版 Macの場合 ``` brew install starkandwayne/cf/uaa-cli ``` Ubuntuの場合 ``` wget -q -O - https://raw.githubusercontent.com/starkandwayne/homebrew-cf/master/public.key | apt-key add - echo "deb http://apt.starkandwayne.com stable main" | tee /etc/apt/sources.list.d/starkandwayne.list apt-get update apt-get install uaa-cli ``` 接続先UAAの設定 ``` uaac target https://demo-uaa.cfapps.io ``` `uaa_admin`クライアントのClient Credentials Grant Typeでアクセストークンを取得してみます。 ``` ADMIN_SECRET=$(bosh int --path /admin_client_secret credentials.yml) uaa get-client-credentials-token uaa_admin -s ${ADMIN_SECRET} ``` クライアント一覧表示 ``` uaa clients ``` 出力結果 ```json [ { "client_id": "demo_dev", "scope": [ "openid", "role" ], "resource_ids": [ "none" ], "authorized_grant_types": [ "authorization_code", "refresh_token", "password" ], "redirect_uri": [ "http://localhost:8080/login/oauth2/code/uaa" ], "authorities": [ "uaa.none" ], "name": "Demo Dev", "lastModified": 1536702358000 }, { "client_id": "uaa_admin", "scope": [ "uaa.none" ], "resource_ids": [ "none" ], "authorized_grant_types": [ "client_credentials" ], "authorities": [ "clients.read", "password.write", "clients.secret", "clients.write", "uaa.admin", "scim.write", "scim.read" ], "lastModified": 1536702358000 } ] ``` ユーザー一覧表示 ```` uaac users ```` 出力結果 ```json [ { "id": "925083e7-81cd-4d87-8751-cb631459f390", "meta": { "version": 4, "created": "2018-09-10T17:44:26.000Z", "lastModified": "2018-09-11T21:45:54.000Z" }, "userName": "admin", "name": {}, "emails": [ { "value": "admin", "primary": false } ], "groups": [ { "value": "3764195c-aa98-4d59-9a54-77345f9fbf67", "display": "openid", "type": "DIRECT" }, { "value": "8b256da8-4027-42ee-bd0f-76453c17c93e", "display": "uaa.user", "type": "DIRECT" }, { "value": "8bf4e1fa-28e7-430a-b0ed-bdadbcd1f120", "display": "uaa.offline_token", "type": "DIRECT" }, { "value": "ed19972a-7abc-4c7b-8681-1e7b6ef25acf", "display": "scim.me", "type": "DIRECT" }, { "value": "7ae25c7c-b96a-40a1-9273-ccdeca98ac04", "display": "password.write", "type": "DIRECT" } ], "active": true, "verified": true, "origin": "uaa", "zoneId": "uaa", "passwordLastModified": "2018-09-10T17:44:26.000Z", "schemas": [ "urn:scim:schemas:core:1.0" ] } ] ``` この[ドキュメント](https://ultimate-guide-to-uaa-staging.cfapps.io/)が詳しいです。 ### Spring Bootと連携 ``` curl https://start.spring.io/starter.tgz \ -d bootVersion=2.0.5.RELEASE \ -d dependencies=web,security \ -d artifactId=demo-boot-uaa \ -d name=demo-boot-uaa \ -d baseDir=demo-boot-uaa \ -d packageName=com.example.demobootuaa \ -d applicationName=DemoBootUaaApplication | tar -xzvf - ``` Spring Boot 2.0の場合は次の`dependenciey`を追加 ```xml org.springframework.security spring-security-oauth2-client org.springframework.security spring-security-oauth2-jose ``` Spring Boot 2.1からは次の`dependenciey`を追加でOK ```xml org.springframework.boot spring-boot-starter-oauth2-oidc-client ``` Spring Boot 2.0の場合は`application.propertis`に次の設定を追加 ```properties spring.security.oauth2.client.provider.uaa.authorization-uri=https://demo-uaa.cfapps.io/oauth/authorize spring.security.oauth2.client.provider.uaa.token-uri=https://demo-uaa.cfapps.io/oauth/token spring.security.oauth2.client.provider.uaa.user-info-uri=https://demo-uaa.cfapps.io/userinfo spring.security.oauth2.client.provider.uaa.jwk-set-uri=https://demo-uaa.cfapps.io/token_keys spring.security.oauth2.client.provider.uaa.user-name-attribute=email spring.security.oauth2.client.registration.uaa.client-id=demo_dev spring.security.oauth2.client.registration.uaa.client-secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx # bosh int credentials.yml --path /demo_dev_client_secretで取得 spring.security.oauth2.client.registration.uaa.scope=openid spring.security.oauth2.client.registration.uaa.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.uaa.client-name=Cloud Foundry UAA spring.security.oauth2.client.registration.uaa.client-authentication-method=basic spring.security.oauth2.client.registration.uaa.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId} ``` Spring Boot 2.1の場合は`application.propertis`に次の設定を追加 ```properties spring.security.oauth2.client.provider.uaa.issuer-uri=https://demo-uaa.cfapps.io/oauth/token spring.security.oauth2.client.provider.uaa.user-name-attribute=email spring.security.oauth2.client.registration.uaa.client-id=demo_dev spring.security.oauth2.client.registration.uaa.client-secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx # bosh int credentials.yml --path /demo_dev_client_secretで取得 spring.security.oauth2.client.registration.uaa.scope=openid spring.security.oauth2.client.registration.uaa.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.uaa.client-name=Cloud Foundry UAA spring.security.oauth2.client.registration.uaa.client-authentication-method=basic spring.security.oauth2.client.registration.uaa.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId} ``` `SecurityConfig.java`を作成して次のコードを記述 ```java package com.example.demobootuaa; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // .anyRequest().authenticated() // .and() // .oauth2Login() // .userInfoEndpoint(); } } ``` 次の`HelloController.java`を追加して、アプリケーションを起動 ```java package com.example.demobootuaa; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/") public Object hi(Authentication authentication) { return authentication; } } ``` [http://localhost:8080](http://localhost:8080)にアクセスすると次の画面にリダイレクトされます。 ![image](https://user-images.githubusercontent.com/106908/45531769-3f9b1500-b82c-11e8-9c1c-5caeff8c0a70.png) とりあえず`admin`ユーザーでログインします。 ![image](https://user-images.githubusercontent.com/106908/45531801-5e99a700-b82c-11e8-8868-259c22f558e2.png) パーミッション確認画面でAUTHORIZEをクリック。 ![image](https://user-images.githubusercontent.com/106908/45531819-78d38500-b82c-11e8-95fe-51eacd9042aa.png) ログインユーザー情報がJSONで出力されます。 ![image](https://user-images.githubusercontent.com/106908/45532729-141a2980-b830-11e8-8e16-d280c0eb7aba.png) ユーザーの作成はCLIでもできますし、ログインフォームの"Create account"からもできます。通知メールが送信されます。 ### ロゴの変更 デフォルトではCloud Foundryのロゴなので、これを変えて見ます。 `resources/images`の下に`product-logo.png`と`square-logo.png`という名前のファイルを作成します。 ```bash mkdir -p resources/images wget -O resources/images/product-logo.png https://github.com/categolj/categolj2-backend/raw/master/logo.png wget -O resources/images/square-logo.png https://github.com/categolj/blog-ui/raw/develop/src/main/resources/public/favicon-96x96.png ``` ロゴを変更するops-file(`ops-files/branding.yml`)を作成。 ```yaml - type: replace path: /login/branding? value: companyName: CategoLJ # product-logo.pngのbase64エンコード文字列 productLogo: iVBORw0KGgoA.............. # square-log.pngのbase64エンコード文字列 squareLogo: iVBORw0KGgoA.............. footerLegalText: Copyright © CategoLJ, 2018. All Rights Reserved. footerLinks: About Us: https://github.com/categolj banner: # product-logo.pngのbase64エンコード文字列 logo: iVBORw0KGgoA.............. text: Welcome to CategoLJ UAA! textColor: "#000000" backgroundColor: "#FFFFFF" link: https://((route)) ``` `embbed-manifest.sh`を更新。 ```bash cat <<'EOF' > embbed-manifest.sh #!/bin/bash set -e bosh int uaa.yml \ -o ops-files/branding.yml \ -o ops-files/smtp.yml \ -o ops-files/add-demo.yml \ -v route=demo-uaa.cfapps.io \ -v smtp_host=smtp.gmail.com \ -v smtp_port=587 \ --vars-store=credentials.yml \ > WEB-INF/classes/uaa.yml jar -uvf ROOT.war .profile WEB-INF resources EOF ``` jarをアップデートして`cf push`! ``` ./embbed-manifest.sh cf push ``` ![image](https://user-images.githubusercontent.com/106908/45535157-8131bd00-b838-11e8-99cb-17ec0a0d37aa.png) --- UAAはLDAPやSAMLとも連携できます。システム内で再利用性の高いコンポーネントなので、認証認可を自分で実装せずUAAの導入を検討してみてください。 一家に一台UAAを。