├── .cfnnagignore ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── auto-approve.yml │ ├── auto-merge.yml │ ├── build.yml │ └── ghpage.yml ├── .gitignore ├── .viperlightignore ├── .viperlightrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── README.zh.md ├── buildspec.yml ├── deployment ├── build-s3-dist.sh └── helper.py ├── docs ├── en │ ├── images │ ├── implementation-guide │ │ ├── additional-resources.md │ │ ├── architecture.md │ │ ├── considerations.md │ │ ├── cost.md │ │ ├── deployment.md │ │ ├── revisions.md │ │ └── tutorials │ │ │ ├── ad-ldap.md │ │ │ └── api-gateway.md │ └── index.md ├── images │ ├── architecture │ │ └── 01-keycloak-on-aws-architecture.png │ ├── aws-solutions.png │ └── implementation-guide │ │ ├── deployment │ │ ├── 16-en-keycloak-index.png │ │ └── 17-en-keycloak-login.png │ │ └── tutorial │ │ ├── ad-ldap │ │ ├── 01-en-ec2-keycloak-security-group-id.png │ │ ├── 02-en-ec2-add-security-group-rules.png │ │ ├── 03-en-keycloak-add-user-provider-01.png │ │ ├── 04-en-keycloak-manger-users.png │ │ ├── 05-en-keycloak-clients.png │ │ ├── 06-en-keycloak-account-console-signin-01.png │ │ ├── 06-en-keycloak-account-console-signin-02.png │ │ ├── 06-en-keycloak-account-console-signin-03.png │ │ └── 07-en-keycloak-user-federation-provider.png │ │ └── api-gateway │ │ ├── 01-en-architecture-diagram.svg │ │ ├── 02-en-keycloak-add-realm.png │ │ ├── 03-en-keycloak-validate-01.png │ │ ├── 03-en-keycloak-validate-02.png │ │ └── 03-en-keycloak-validate-03.png ├── index.html ├── mkdocs.base.yml ├── mkdocs.en.yml ├── mkdocs.zh.yml └── zh │ ├── images │ ├── implementation-guide │ ├── additional-resources.md │ ├── architecture.md │ ├── considerations.md │ ├── cost.md │ ├── deployment.md │ ├── revisions.md │ └── tutorials │ │ ├── ad-ldap.md │ │ └── api-gateway.md │ └── index.md ├── source ├── .env ├── .eslintrc.json ├── .npmignore ├── .versionrc.json ├── cdk.json ├── package-lock.json ├── package.json ├── src │ ├── main.ts │ └── stack.ts ├── test │ ├── __snapshots__ │ │ └── main.test.ts.snap │ └── main.test.ts ├── tsconfig.jest.json ├── tsconfig.json └── version.json ├── tutorials ├── api-gateway │ ├── resources │ │ ├── keycloak.json │ │ ├── policyAllowDocument.json │ │ ├── policyDenyDocument.json │ │ └── realm-export.json │ ├── serverless-express-auth │ │ ├── .gitignore │ │ ├── api-gateway-event.json │ │ ├── app.local.js │ │ ├── event.js │ │ ├── package.json │ │ ├── scripts │ │ │ └── local.js │ │ ├── serverless.yml │ │ ├── src │ │ │ ├── app.js │ │ │ └── lambda.js │ │ ├── webpack.config.js │ │ └── yarn.lock.1 │ ├── serverless-lambda-auth │ │ ├── .gitignore │ │ ├── authorizerUtil.js │ │ ├── babel.config.js │ │ ├── handler.js │ │ ├── package.json │ │ ├── serverless.yml │ │ ├── webpack.config.babel.js │ │ └── yarn.lock.1 │ └── vue-ui │ │ ├── .browserslistrc │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── README.md │ │ ├── babel.config.js │ │ ├── jest.config.js │ │ ├── package.json │ │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── silent-check-sso.html │ │ ├── src │ │ ├── App.vue │ │ ├── components │ │ │ └── HelloWorld.vue │ │ ├── kcConfig.ts │ │ ├── main.ts │ │ ├── shims-tsx.d.ts │ │ └── shims-vue.d.ts │ │ ├── tests │ │ └── unit │ │ │ └── example.spec.ts │ │ ├── tsconfig.json │ │ ├── vue.config.js │ │ └── yarn.lock.1 └── three-layer-subnets.template └── vetur.config.js /.cfnnagignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/.cfnnagignore -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.{js,ts}] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [*.sh] 19 | indent_style = space 20 | indent_size = 4 21 | 22 | # 4 space indentation 23 | [*.py] 24 | indent_style = space 25 | indent_size = 4 26 | 27 | # Tab indentation (no size specified) 28 | [Makefile] 29 | indent_style = tab 30 | 31 | [*.{yml,yaml,json}] 32 | indent_style = space 33 | indent_size = 2 34 | 35 | [*.md] 36 | indent_style = space 37 | indent_size = 4 38 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | versioning-strategy: lockfile-only 5 | directory: /source 6 | schedule: 7 | interval: weekly 8 | labels: 9 | - dependencies 10 | - dependabot 11 | - auto-merge 12 | -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | name: Auto Approve 2 | 3 | on: 4 | pull_request_target 5 | 6 | jobs: 7 | auto-approve: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: hmarr/auto-approve-action@v2.0.0 11 | if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' 12 | with: 13 | github-token: "${{ secrets.GITHUB_TOKEN }}" 14 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Auto Merge 2 | on: 3 | pull_request: 4 | types: 5 | - labeled 6 | - unlabeled 7 | - synchronize 8 | - opened 9 | - edited 10 | - ready_for_review 11 | - reopened 12 | - unlocked 13 | pull_request_review: 14 | types: 15 | - submitted 16 | check_suite: 17 | types: 18 | - completed 19 | status: {} 20 | jobs: 21 | automerge: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: automerge 25 | uses: "pascalgn/automerge-action@v0.13.1" 26 | env: 27 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 28 | MERGE_LABELS: "auto-merge,!wip,!work in progress" 29 | MERGE_RETRY_SLEEP: "60000" 30 | MERGE_DELETE_BRANCH: "true" 31 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: {} 4 | workflow_dispatch: {} 5 | push: 6 | branches: 7 | - master 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | env: 12 | CI: "true" 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: "18" 20 | - name: Build 21 | run: ./deployment/build-s3-dist.sh dist-bucket solution-name latest 22 | 23 | build-examples: 24 | runs-on: ubuntu-latest 25 | env: 26 | CI: "true" 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v2 30 | - name: Setup Node.js 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: "18" 34 | - name: Build vue-ui 35 | working-directory: tutorials/api-gateway/vue-ui 36 | run: yarn && yarn build 37 | - name: Build serverless-express-auth 38 | working-directory: tutorials/api-gateway/serverless-express-auth 39 | run: yarn && yarn build 40 | - name: Build serverless-lambda-auth 41 | working-directory: tutorials/api-gateway/serverless-lambda-auth 42 | run: yarn && yarn build 43 | -------------------------------------------------------------------------------- /.github/workflows/ghpage.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master # Set a branch name to trigger deployment 7 | workflow_dispatch: {} 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod 16 | 17 | - name: Setup Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.8' 21 | 22 | - name: Install dependencies 23 | run: | 24 | python3 -m pip install mkdocs # install mkdocs 25 | python3 -m pip install mkdocs-material # install material theme 26 | python3 -m pip install mkdocs-macros-plugin # install macros plugin 27 | python3 -m pip install mkdocs-include-markdown-plugin # install include-markdown 28 | - name: Build mkdocs 29 | run: | 30 | mkdocs build -f ./docs/mkdocs.en.yml 31 | mkdocs build -f ./docs/mkdocs.zh.yml 32 | cp -av ./docs/index.html ./docs/site 33 | - name: Deploy 34 | uses: peaceiris/actions-gh-pages@v3 35 | if: ${{ github.ref == 'refs/heads/master' }} 36 | with: 37 | github_token: ${{ secrets.GITHUB_TOKEN }} 38 | publish_dir: ./docs/site 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | !/.projen/tasks.json 3 | !/.projen/deps.json 4 | !/package.json 5 | !/LICENSE 6 | !/.npmignore 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | # nyc test coverage 27 | .nyc_output 28 | # Compiled binary addons (https://nodejs.org/api/addons.html) 29 | build/Release 30 | # Dependency directories 31 | jspm_packages/ 32 | # TypeScript cache 33 | *.tsbuildinfo 34 | # Optional eslint cache 35 | .eslintcache 36 | # Output of 'npm pack' 37 | *.tgz 38 | # Yarn Integrity file 39 | .yarn-integrity 40 | # parcel-bundler cache (https://parceljs.org/) 41 | .cache 42 | !/.projenrc.js 43 | !version.json 44 | !/.versionrc.json 45 | # jest-junit artifacts 46 | /test-reports/ 47 | junit.xml 48 | /coverage 49 | !/test 50 | !/.github/workflows/build.yml 51 | !/.mergify.yml 52 | !/.github/dependabot.yml 53 | !/.github/pull_request_template.md 54 | !/.github/workflows/rebuild-bot.yml 55 | !/tsconfig.json 56 | /lib 57 | !/src 58 | /dist 59 | !/tsconfig.jest.json 60 | !/.eslintrc.json 61 | cdk.out/ 62 | .cdk.staging/ 63 | .parcel-cache/ 64 | !/cdk.json 65 | global-s3-assets/ 66 | regional-s3-assets/ 67 | cdk.out/ 68 | cdk.*.out/ 69 | -------------------------------------------------------------------------------- /.viperlightignore: -------------------------------------------------------------------------------- 1 | # Ignore Config used with code.amazon.com 2 | Config 3 | 4 | # Use of opensource-codeofconduct email for amazon is expected 5 | CODE_OF_CONDUCT.md 6 | CONTRIBUTING.md 7 | 8 | cdk.out/ 9 | ^dist/ 10 | node_modules/ 11 | package-lock.json 12 | yarn.lock 13 | -------------------------------------------------------------------------------- /.viperlightrc: -------------------------------------------------------------------------------- 1 | { 2 | "all": true, 3 | "failOn": "medium" 4 | } 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [2.1.0](https://github.com/aws-samples/keycloak-on-aws/compare/v2.0.3...v2.1.0) (2022-06-30) 6 | 7 | 1. Upgrade Keycloak to version 16.1.1 8 | 2. Upgrade aws-cdk to version 1.160.0 9 | 3. Upgrade cdk-keycloak to version 0.2.40 10 | 11 | ### [2.0.3](https://github.com/aws-samples/keycloak-on-aws/compare/v2.0.2...v2.0.3) (2021-05-23) 12 | 13 | 14 | ### Bug Fixes 15 | 16 | * build error ([a74402a](https://github.com/aws-samples/keycloak-on-aws/commit/a74402a8eee3c5632efd9cf88ff4b0b4c83e73d7)) 17 | * deps security issues ([deeba98](https://github.com/aws-samples/keycloak-on-aws/commit/deeba9885deed9a714ba216050ac9b87b794534b)) 18 | * deps security issues ([#94](https://github.com/aws-samples/keycloak-on-aws/issues/94)) ([437f57b](https://github.com/aws-samples/keycloak-on-aws/commit/437f57b626358de5aab7aaf0ee099385f7637f8a)) 19 | * postcss deps alert ([f18dc13](https://github.com/aws-samples/keycloak-on-aws/commit/f18dc139afe6b2b9a28fa05cdb013542347a8a22)) 20 | * realm-export.json missing user info ([a0787f7](https://github.com/aws-samples/keycloak-on-aws/commit/a0787f761a2788b1094e3e85558ac70fb1eea4a3)) 21 | * security issue ([683c6f6](https://github.com/aws-samples/keycloak-on-aws/commit/683c6f6a87e6ad2c81243b9a3d06c36659ac8781)) 22 | * xmlhttprequest-ssl security issue ([7fefb15](https://github.com/aws-samples/keycloak-on-aws/commit/7fefb15befff2b0ca89346f0878984712abdc968)) 23 | 24 | ### [2.0.2](https://github.com/aws-samples/keycloak-on-aws/compare/v2.0.1...v2.0.2) (2021-02-26) 25 | 26 | ### [2.0.1](https://github.com/aws-samples/keycloak-on-aws/compare/v2.0.0...v2.0.1) (2021-02-26) 27 | 28 | 29 | ### Features 30 | 31 | * add solution id in description and add JavaOpts param ([ff5c6d0](https://github.com/aws-samples/keycloak-on-aws/commit/ff5c6d05bc65d7e54aef362faa4008f972b063e6)) 32 | * add three-layer-subnets.template ([bc723a6](https://github.com/aws-samples/keycloak-on-aws/commit/bc723a6431584d64ba33695402bd9e16d44f21ba)) 33 | 34 | 35 | ### Bug Fixes 36 | 37 | * .github/dependabot.yml wrong config ([d2a00d3](https://github.com/aws-samples/keycloak-on-aws/commit/d2a00d30dee2b1577abba5c37f555b260133b5c0)) 38 | 39 | ## [2.0.0](https://github.com/aws-samples/keycloak-on-aws/compare/v2.0.0-beta.2...v2.0.0) (2021-02-19) 40 | 41 | ## [2.0.0-beta.2](https://github.com/aws-samples/keycloak-on-aws/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2021-02-18) 42 | 43 | 44 | ### Bug Fixes 45 | 46 | * upgrade cdk-keycloak [#6](https://github.com/aws-samples/keycloak-on-aws/issues/6) ([e4225df](https://github.com/aws-samples/keycloak-on-aws/commit/e4225dfaae1650e5186e05905628eb4789d302e5)) 47 | 48 | ## [2.0.0-beta.1](https://github.com/aws-samples/keycloak-on-aws/compare/v2.0.0-alpha.2...v2.0.0-beta.1) (2021-02-18) 49 | 50 | ## [2.0.0-alpha.2](v2.0.0-alpha.1...v2.0.0-alpha.2) (2021-02-17) 51 | 52 | ## [2.0.0-alpha.1](v2.0.0-alpha.0...v2.0.0-alpha.1) (2021-02-08) 53 | 54 | ## [2.0.0-alpha.0](v1.0.0...v2.0.0-alpha.0) (2021-02-04) 55 | 56 | ## [1.0.0] - 2020-06-18 57 | ### Inited 58 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open or recently closed issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/sample-repo/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](./LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except 3 | in compliance with the License. A copy of the License is located at http://www.apache.org/licenses/ 4 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, 5 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the 6 | specific language governing permissions and limitations under the License. 7 | 8 | ********************** 9 | THIRD PARTY COMPONENTS 10 | ********************** 11 | This software includes third party software subject to the following copyrights: 12 | 13 | keycloak - Apache 2.0 License. 14 | Ref URL: https://github.com/keycloak/keycloak/blob/master/LICENSE.txt 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keycloak on AWS 2 | 3 | [![Build](https://github.com/aws-samples/keycloak-on-aws/actions/workflows/build.yml/badge.svg)](https://github.com/aws-samples/keycloak-on-aws/actions/workflows/build.yml) 4 | 5 | [中文](./README.zh.md) 6 | 7 | This is a solution for deploying [Keycloak](https://www.keycloak.org/) to AWS with high availability. Keycloak is a single sign-on (SSO) solution for web applications and RESTful web services. Keycloak's goal is to simplify security so that application developers can easily protect applications and services already deployed in their organizations. Out of the box, Keycloak provides security features that developers would normally have to write for themselves and can be easily customized for the individual needs of the organization. Keycloak provides a customizable user interface for login, registration, administration and account management. You can also use Keycloak as an integration platform to hook into existing LDAP and Active Directory servers. You can also delegate authentication to third-party identity providers, such as Facebook and Google+. 8 | 9 | ## Architecture diagram 10 | 11 | ![architecture](./docs/images/architecture/01-keycloak-on-aws-architecture.png) 12 | 13 | - A highly available architecture that spans two [Availability Zones][Availability Zones]. 14 | - A [Amazon Virtual Private Cloud (Amazon VPC)][Amazon VPC] configured with public and private subnets, according to AWS best practices, to provide you with your own virtual network on AWS. 15 | - In the public subnets, managed Network Address Translation (NAT) gateways to allow outbound internet access for resources in the private subnets. 16 | - In the private subnets: 17 | - [Amazon Elastic Container Service (Amazon ECS)][Amazon ECS] tasks running with [AWS Fargate][AWS Fargate] behind the [Application Load Balancer][Application Load Balancer]. 18 | - [Amazon Aurora Serverless MySQL-Compatible][Amazon Aurora Serverless] database cluster or [Amazon Aurora MySQL-Compatible][Amazon Aurora] cluster. 19 | - [IAM][AWS Identity and Access Management] role for the [Amazon ECS][Amazon ECS] service. 20 | - Secrets from [AWS Secrets Manager][AWS Secrets Manager] for [Keycloak][Keycloak] console login and database connection. 21 | - [AWS Certificate Manager (ACM)][Amazon Certificate Manager], which uses your existing certificate for the custom domain name on the [Application Load Balancer][Application Load Balancer]. 22 | - [Amazon Route 53][Amazon Route 53] alias record, which is required for the custom domain name. 23 | 24 | ## Quick start 25 | 26 | * [Implementation Guide](https://aws-samples.github.io/keycloak-on-aws/en/implementation-guide/deployment/) 27 | 28 | ## License 29 | 30 | Copyright 2021 Amazon.com, Inc. or its affiliates. 31 | 32 | Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 33 | 34 | http://www.apache.org/licenses/ 35 | 36 | This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and limitations under the License. 37 | 38 | [Availability Zones]: https://aws.amazon.com/about-aws/global-infrastructure/regions_az/ 39 | [AWS CloudFormation]: https://aws.amazon.com/cloudformation/ 40 | [Amazon VPC]: https://aws.amazon.com/vpc/ 41 | [AWS Fargate]: https://aws.amazon.com/fargate/ 42 | [Amazon ECS]: https://aws.amazon.com/ecs/ 43 | [Amazon ECR]: https://aws.amazon.com/ecr/ 44 | [Application Load Balancer]: https://aws.amazon.com/elasticloadbalancing/application-load-balancer/ 45 | [Amazon Certificate Manager]: https://aws.amazon.com/certificate-manager/ 46 | [AWS Identity and Access Management]: https://aws.amazon.com/iam/ 47 | [Amazon Route 53]: https://aws.amazon.com/route53/ 48 | [Amazon Aurora]: https://aws.amazon.com/rds/aurora/ 49 | [Amazon Aurora Serverless]: https://aws.amazon.com/rds/aurora/serverless/ 50 | [AWS Secrets Manager]: https://aws.amazon.com/secrets-manager/ 51 | [Keycloak]: https://www.keycloak.org/ 52 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # Keycloak on AWS 2 | 3 | [![Build](https://github.com/aws-samples/keycloak-on-aws/actions/workflows/build.yml/badge.svg)](https://github.com/aws-samples/keycloak-on-aws/actions/workflows/build.yml) 4 | 5 | [English](./README.md) 6 | 7 | 这是一个将 [Keycloak](https://www.keycloak.org/) 高可用部署在 AWS 的解决方案。Keycloak 是针对 Web 应用程序和 RESTful Web 服务的单一登录(SSO)解决方案。 Keycloak 的目标是简化安全性,以便应用程序开发人员可以轻松保护已部署在组织中的应用程序和服务。开箱即用地提供了开发人员通常必须为自己编写的安全功能,可以轻松地针对组织的个性化需求进行定制。 Keycloak 提供可自定义的用户界面,用于登录,注册,管理和帐户管理。 您还可以将 Keycloak 用作集成平台,以将其挂接到现有的 LDAP 和 Active Directory 服务器中。 您还可以将身份验证委派给第三方身份提供商,例如 Facebook 和 Google+ 。 8 | 9 | ## 架构图 10 | 11 | ![architecture](./docs/images/architecture/01-keycloak-on-aws-architecture.png) 12 | 13 | - 在两个 [可用区][Availability Zones] 部署的高可用架构; 14 | - 根据 AWS 最佳实践在 [Amazon Virtual Private Cloud (Amazon VPC)][Amazon VPC] 中配置了公有和私有子网进行资源隔离; 15 | - 处在私有子网中的资源通过公有子网中的NAT网关连接到互联网,但不能接收来自互联网的未经请求的入站连接; 16 | - 在私有子网中: 17 | - 使用 [AWS Fargate][AWS Fargate] 运行和扩展 [Amazon ECS][Amazon ECS] 容器工作负载; 18 | - 使用 [Application Load Balancer][Application Load Balancer] 负载均衡请求流量; 19 | - 部署 [Amazon Aurora Serverless MySQL-Compatible][Amazon Aurora Serverless] 或 [Amazon Aurora MySQL-Compatible][Amazon Aurora] 数据库集群。 20 | - 为 [Amazon ECS][Amazon ECS] 服务创建 [IAM][AWS Identity and Access Management] 角色; 21 | - 通过 [AWS Secrets Manager][AWS Secrets Manager] 管理 [Keycloak][Keycloak] 控制台登录和数据库连接密钥; 22 | - 通过 [AWS Certificate Manager (ACM)][Amazon Certificate Manager] 将现有的证书应用到 [Application Load Balancer][Application Load Balancer] 的域名上; 23 | - 在 [Amazon Route 53][Amazon Route 53] 中添加别名记录,用于访问Keycloak控制台。 24 | 25 | ## 快速启动 26 | 27 | [部署解决方案](https://aws-samples.github.io/keycloak-on-aws/zh/implementation-guide/deployment/) 28 | 29 | ## License 30 | 31 | Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 32 | 33 | Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 34 | 35 | http://www.apache.org/licenses/ 36 | 37 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and limitations under the License. 38 | 39 | [Availability Zones]: https://aws.amazon.com/about-aws/global-infrastructure/regions_az/ 40 | [AWS CloudFormation]: https://amazonaws.cn/cloudformation/ 41 | [Amazon VPC]: https://amazonaws.cn/vpc/ 42 | [AWS Fargate]: https://amazonaws.cn/fargate/ 43 | [Amazon ECS]: https://amazonaws.cn/ecs/ 44 | [Amazon ECR]: https://amazonaws.cn/ecr/ 45 | [Application Load Balancer]: https://amazonaws.cn/elasticloadbalancing/application-load-balancer/ 46 | [Amazon Certificate Manager]: https://amazonaws.cn/certificate-manager/ 47 | [AWS Identity and Access Management]: https://amazonaws.cn/iam/ 48 | [Amazon Route 53]: https://amazonaws.cn/route53/ 49 | [Amazon Aurora]: https://amazonaws.cn/rds/aurora 50 | [Amazon Aurora Serverless]: https://amazonaws.cn/rds/aurora/serverless/ 51 | [AWS Secrets Manager]: https://amazonaws.cn/secrets-manager/ 52 | [Keycloak]: https://www.keycloak.org/ 53 | -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 10 7 | build: 8 | commands: 9 | - . ./source/.env 10 | - ./deployment/build-s3-dist.sh ${DIST_OUTPUT_BUCKET} ${SOLUTION_NAME} ${VERSION} 11 | - mkdir -p deployment/open-source/ && touch deployment/open-source/.empty 12 | post_build: 13 | commands: 14 | - aws s3 cp s3://solutions-build-assets/changelog-spec.yml buildspec.yml || true 15 | artifacts: 16 | files: 17 | - .git/**/* 18 | - deployment/**/* 19 | - buildspec.yml 20 | - CHANGELOG.md 21 | - .cfnnagignore 22 | -------------------------------------------------------------------------------- /deployment/build-s3-dist.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | title() { 5 | echo "------------------------------------------------------------------------------" 6 | echo $* 7 | echo "------------------------------------------------------------------------------" 8 | } 9 | 10 | run() { 11 | >&2 echo ::$* 12 | $* 13 | } 14 | 15 | __dir="$(cd "$(dirname $0)";pwd)" 16 | SRC_PATH="${__dir}/../source" 17 | CDK_OUT_PATH="${__dir}/cdk.out" 18 | 19 | if [ -z "$1" ] || [ -z "$2" ]; then 20 | echo "Parameters not enough" 21 | echo "Example: $(basename $0) [VERSION]" 22 | exit 1 23 | fi 24 | 25 | export BUCKET_NAME=$1 26 | export SOLUTION_NAME=$2 27 | if [ -z "$3" ]; then 28 | export VERSION="v$(jq -r '.version' ${SRC_PATH}/version.json)" 29 | # export VERSION=$(git describe --tags || echo latest) 30 | else 31 | export VERSION=$3 32 | fi 33 | export GLOBAL_S3_ASSETS_PATH="${__dir}/global-s3-assets" 34 | export REGIONAL_S3_ASSETS_PATH="${__dir}/regional-s3-assets" 35 | 36 | title "init env" 37 | 38 | run rm -rf ${GLOBAL_S3_ASSETS_PATH} && run mkdir -p ${GLOBAL_S3_ASSETS_PATH} 39 | run rm -rf ${REGIONAL_S3_ASSETS_PATH} && run mkdir -p ${REGIONAL_S3_ASSETS_PATH} 40 | run rm -rf ${CDK_OUT_PATH} 41 | 42 | echo "BUCKET_NAME=${BUCKET_NAME}" 43 | echo "SOLUTION_NAME=${SOLUTION_NAME}" 44 | echo "VERSION=${VERSION}" 45 | echo "${VERSION}" > ${GLOBAL_S3_ASSETS_PATH}/version 46 | 47 | title "cdk synth" 48 | 49 | run cd ${SRC_PATH} 50 | run npm i 51 | run npm run test 52 | 53 | export USE_BSS=true 54 | # How to config https://github.com/wchaws/cdk-bootstrapless-synthesizer/blob/main/API.md 55 | export BSS_TEMPLATE_BUCKET_NAME="${BUCKET_NAME}" 56 | export BSS_FILE_ASSET_BUCKET_NAME="${BUCKET_NAME}-\${AWS::Region}" 57 | export BSS_FILE_ASSET_PREFIX="${SOLUTION_NAME}/${VERSION}/" 58 | export BSS_FILE_ASSET_REGION_SET="us-east-1,${BSS_FILE_ASSET_REGION_SET}" 59 | 60 | run npm run synth -- --output ${CDK_OUT_PATH} 61 | run ${__dir}/helper.py ${CDK_OUT_PATH} 62 | 63 | title "tips!" 64 | 65 | echo "To test your cloudformation template" 66 | echo "make sure you have the following bucket exists in your account" 67 | echo " - ${BUCKET_NAME}" 68 | echo ${BSS_FILE_ASSET_REGION_SET} | tr ',' '\n' | xargs -I {} echo " - ${BUCKET_NAME}-{}" 69 | echo "run \`aws s3 cp --recursive ${GLOBAL_S3_ASSETS_PATH} s3://${BUCKET_NAME}/${SOLUTION_NAME}/${VERSION}\`" 70 | echo "run \`echo \"${BSS_FILE_ASSET_REGION_SET}\" | tr ',' '\n' | xargs -t -I {} aws s3 cp --recursive --region {} ${REGIONAL_S3_ASSETS_PATH} s3://${BUCKET_NAME}-{}/${SOLUTION_NAME}/${VERSION}\`" 71 | -------------------------------------------------------------------------------- /deployment/helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | import json 6 | import glob 7 | import subprocess 8 | 9 | 10 | GLOBAL_S3_ASSETS_PATH = os.environ['GLOBAL_S3_ASSETS_PATH'] 11 | REGIONAL_S3_ASSETS_PATH = os.environ['REGIONAL_S3_ASSETS_PATH'] 12 | 13 | 14 | class Color(object): 15 | ISATTY = os.isatty(1) 16 | COLORS = { 17 | 'red': '\x1b[31m', 18 | 'green': '\x1b[32m', 19 | 'yellow': '\x1b[33m', 20 | 'blue': '\x1b[34m', 21 | 'reset': '\x1b[0m' 22 | } 23 | 24 | @staticmethod 25 | def c(s, code): 26 | if Color.ISATTY: 27 | return Color.COLORS[code] + s + Color.COLORS['reset'] 28 | return s 29 | 30 | @staticmethod 31 | def red(s): 32 | return Color.c(s, 'red') 33 | 34 | @staticmethod 35 | def green(s): 36 | return Color.c(s, 'green') 37 | 38 | @staticmethod 39 | def yellow(s): 40 | return Color.c(s, 'yellow') 41 | 42 | @staticmethod 43 | def blue(s): 44 | return Color.c(s, 'blue') 45 | 46 | 47 | def get_file_assets(filename): 48 | with open(filename, 'r') as fp: 49 | assets = json.load(fp) 50 | files = assets['files'] 51 | 52 | def _add_key(k, v): 53 | v['_id'] = k 54 | return v 55 | 56 | return [_add_key(k, v) for k, v in files.items()] 57 | 58 | 59 | def sh(*args): 60 | return subprocess.call(*args, shell=True) 61 | 62 | 63 | def zip(src, dst): 64 | print(f'{Color.yellow("[zip]")} {Color.green(f"{src} => {dst}")}') 65 | sh(f'cd {src} && zip -r {dst} .') 66 | 67 | 68 | def cp(src, dst): 69 | print(f'{Color.yellow("[cp]")} {Color.green(f"{src} => {dst}")}') 70 | sh(f'cp {src} {dst}') 71 | 72 | 73 | def main(): 74 | dir_in = os.path.abspath(sys.argv[1]) 75 | assets = glob.glob(os.path.join(dir_in, '*.assets.json')) 76 | 77 | for asset in assets: 78 | print(f'from {Color.blue(asset)}') 79 | file_assets = get_file_assets(asset) 80 | for file in file_assets: 81 | source = file['source'] 82 | src = os.path.join(dir_in, source['path']) 83 | if src.endswith('template.json'): 84 | dst = os.path.abspath(os.path.join(GLOBAL_S3_ASSETS_PATH, file['_id'].replace('.json', ''))) 85 | else: 86 | dst = os.path.abspath(os.path.join(REGIONAL_S3_ASSETS_PATH, file['_id'])) 87 | if source['packaging'] == 'zip': 88 | zip(src, dst) 89 | elif source['packaging'] == 'file': 90 | cp(src, dst) 91 | 92 | 93 | if __name__ == '__main__': 94 | main() 95 | -------------------------------------------------------------------------------- /docs/en/images: -------------------------------------------------------------------------------- 1 | ../images -------------------------------------------------------------------------------- /docs/en/implementation-guide/additional-resources.md: -------------------------------------------------------------------------------- 1 | # AWS Services 2 | 3 | * [AWS CloudFormation](https://aws.amazon.com/cloudformation/) 4 | * [Amazon ECS](https://aws.amazon.com/ecs/) 5 | * [Amazon ECR](https://aws.amazon.com/ecr/) 6 | * [Amazon Certificate Manager (ACM)](https://aws.amazon.com/certificate-manager/) 7 | * [AWS Identity and Access Management (IAM)](https://aws.amazon.com/iam/) 8 | * [Amazon Route 53](https://aws.amazon.com/route53/) 9 | * [Amazon Aurora](https://aws.amazon.com/rds/aurora) 10 | * [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) 11 | * [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/) 12 | * [AWS Fargate](https://aws.amazon.com/fargate/) 13 | -------------------------------------------------------------------------------- /docs/en/implementation-guide/architecture.md: -------------------------------------------------------------------------------- 1 | Deploying this solution with the default parameters builds the following environment in the AWS Cloud. 2 | 3 | ![architecture](../images/architecture/01-keycloak-on-aws-architecture.png) 4 | 5 | Figure 1: Solution architecture 6 | 7 | This solution deploys the AWS CloudFormation template in your AWS Cloud account and completes the following settings. 8 | 9 | - A highly available architecture that spans two [Availability Zones][Availability Zones]. 10 | - A [Amazon Virtual Private Cloud (Amazon VPC)][Amazon VPC] configured with public and private subnets, according to AWS best practices, to provide you with your own virtual network on AWS. 11 | - In the public subnets, managed Network Address Translation (NAT) gateways to allow outbound internet access for resources in the private subnets. 12 | - In the private subnets: 13 | - [Amazon Elastic Container Service (Amazon ECS)][Amazon ECS] tasks running with [AWS Fargate][AWS Fargate] behind the [Application Load Balancer][Application Load Balancer]. 14 | - [Amazon Aurora Serverless MySQL-Compatible][Amazon Aurora Serverless] database cluster or [Amazon Aurora MySQL-Compatible][Amazon Aurora] cluster. 15 | - [IAM][AWS Identity and Access Management] role for the [Amazon ECS][Amazon ECS] service. 16 | - Secrets from [AWS Secrets Manager][AWS Secrets Manager] for [Keycloak][Keycloak] console login and database connection. 17 | - [AWS Certificate Manager (ACM)][Amazon Certificate Manager], which uses your existing certificate for the custom domain name on the [Application Load Balancer][Application Load Balancer]. 18 | - [Amazon Route 53][Amazon Route 53] alias record, which is required for the custom domain name. 19 | 20 | [Availability Zones]: https://aws.amazon.com/about-aws/global-infrastructure/regions_az/ 21 | [AWS CloudFormation]: https://aws.amazon.com/cloudformation/ 22 | [Amazon VPC]: https://aws.amazon.com/vpc/ 23 | [AWS Fargate]: https://aws.amazon.com/fargate/ 24 | [Amazon ECS]: https://aws.amazon.com/ecs/ 25 | [Amazon ECR]: https://aws.amazon.com/ecr/ 26 | [Application Load Balancer]: https://aws.amazon.com/elasticloadbalancing/application-load-balancer/ 27 | [Amazon Certificate Manager]: https://aws.amazon.com/certificate-manager/ 28 | [AWS Identity and Access Management]: https://aws.amazon.com/iam/ 29 | [Amazon Route 53]: https://aws.amazon.com/route53/ 30 | [Amazon Aurora]: https://aws.amazon.com/rds/aurora/ 31 | [Amazon Aurora Serverless]: https://aws.amazon.com/rds/aurora/serverless/ 32 | [AWS Secrets Manager]: https://aws.amazon.com/secrets-manager/ 33 | [Keycloak]: https://www.keycloak.org/ 34 | -------------------------------------------------------------------------------- /docs/en/implementation-guide/considerations.md: -------------------------------------------------------------------------------- 1 | 2 | ## Regional deployments 3 | This solution uses services which may not be currently available in all AWS Regions. Launch this solution in an AWS Region where required services are available. For the most current availability by Region, refer to the [AWS Regional Services List][services]. 4 | 5 | Because the solution has Amazon Aurora MySQL-Compatible, Amazon Aurora Serverless v1 MySQL-Compatible and Amazon Aurora Serverless v2 MySQL-Compatible to choose from, when deploying with CloudFormation, you need to check whether the region supports your choice. 6 | 7 | For Aurora Serverless deployments, Aurora Serverless v2 MySQL-Compatible is provided by default in the CloudFormation templates. Aurora Serverless v2 scales more quickly and in a more granular way and also has more compatibility with other Aurora features such as reader DB instances. For more information, see [Comparison of Aurora Serverless v2 and Aurora Serverless v1 requirements][comparisons]. 8 | 9 | **Supported regions for database deployment in AWS Global Regions** 10 | 11 | | Region ID | Region Name | Amazon Aurora MySQL-Compatible | Amazon Aurora Serverless v1 MySQL-Compatible |Amazon Aurora Serverless v2 MySQL-Compatible | 12 | | -------------- | ------------------------ | :------------: | :----------------------: | :----------------------: | 13 | | us-east-1 | US East (N. Virginia) | ✔ | ✔ | ✔ | 14 | | us-east-2 | US East (Ohio) | ✔ | ✔ | ✔ | 15 | | us-west-1 | US West (N. California) | ✔ | ✔ | ✔ | 16 | | us-west-2 | US West (Oregon) | ✔ | ✔ | ✔ | 17 | | af-south-1 | Africa (Cape Town) | ✔ | | ✔ | 18 | | ap-east-1 | Asia Pacific (Hongkong) | ✔ | | ✔ | 19 | | ap-south-2 | Asia Pacific (Hyderabad) | ✔ | | ✔ | 20 | | ap-southeast-3 | Asia Pacific (Jakarta) | ✔ | | ✔ | 21 | | ap-southeast-4 | Asia Pacific (Melbourne) | ✔ | | ✔ | 22 | | ap-south-1 | Asia Pacific (Mumbai) | ✔ | ✔ | ✔ | 23 | | ap-northeast-3 | Asia Pacific (Osaka) | ✔ | | ✔ | 24 | | ap-northeast-2 | Asia Pacific (Seoul) | ✔ | ✔ | ✔ | 25 | | ap-southeast-1 | Asia Pacific (Singapore) | ✔ | ✔ | ✔ | 26 | | ap-southeast-2 | Asia Pacific (Sydney) | ✔ | ✔ | ✔ | 27 | | ap-northeast-1 | Asia Pacific (Tokyo) | ✔ | ✔ | ✔ | 28 | | ca-central-1 | Canada (Central) | ✔ | ✔ | ✔ | 29 | | eu-central-1 | Europe (Frankfurt) | ✔ | ✔ | ✔ | 30 | | eu-west-1 | Europe (Ireland) | ✔ | ✔ | ✔ | 31 | | eu-west-2 | Europe (London) | ✔ | ✔ | ✔ | 32 | | eu-south-1 | Europe (Milan) | ✔ | | ✔ | 33 | | eu-west-3 | Europe (Paris) | ✔ | ✔ | ✔ | 34 | | eu-south-2 | Europe (Spain) | ✔ | | ✔ | 35 | | eu-north-1 | Europe (Stockholm) | ✔ | | ✔ | 36 | | eu-central-2 | Europe (Zurich) | ✔ | | ✔ | 37 | | il-central-1 | Israel (Tel Aviv) | ✔ | | ✔ | 38 | | me-south-1 | Middle East (Bahrain) | ✔ | | ✔ | 39 | | me-central-1 | Middle East (UAE) | ✔ | | ✔ | 40 | | sa-east-1 | South America (Sao Paulo)| ✔ | | ✔ | 41 | 42 | 43 | **Supported regions for database deployment in AWS China Regions** 44 | 45 | | Region ID | Region Name | Amazon Aurora MySQL-Compatible | Amazon Aurora Serverless v1 MySQL-Compatible |Amazon Aurora Serverless v2 MySQL-Compatible | 46 | | -------------- | ----------------------------------------- | :------------: | :----------------------: | :----------------------: | 47 | | cn-north-1 | China (Beijing) Region Operated by Sinnet | ✔ | | ✔ | 48 | | cn-northwest-1 | China (Ningxia) Region Operated by NWCD | ✔ | ✔ | ✔ | 49 | 50 | 51 | [services]: https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/?nc1=h_ls 52 | [comparisons]: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html#Serverless.v1-v2-requirements -------------------------------------------------------------------------------- /docs/en/implementation-guide/cost.md: -------------------------------------------------------------------------------- 1 | # Cost Estimation 2 | 3 | You will be responsible for the cost of using each of the AWS services when running the solution. As of July 2022, the main factors affecting the solution cost include: 4 | 5 | - AWS Fargate 6 | - Amazon Aurora MySQL-Compatible 7 | - Application Load Balancer 8 | ## Example 1 9 | 10 | Your database instance uses Amazon Aurora MySQL-Compatible, with Multi-AZ deployment option, OnDemand pricing model. The monthly cost of using this solution in the US East (N. Virginia) region is shown below: 11 | 12 | | Service | Dimensions | Cost (MLY) | 13 | | ------- | --- | ---: | 14 | | AWS Fargate | 1. 2 ECS Tasks that run for 24 hours every day.
2. Each ECS task uses 1 vCPU, 2GB memory, and 20GB ephemeral storage. | $ 72.08 | 15 | | Amazon Aurora MySQL-Compatible | 1. Use db.t3.medium (2 vCPU, 4 GB memory).
2. A Multi-AZ DB cluster has a writer DB instance and a reader DB instances in two separate Availability Zones in the same AWS Region.
3. Pricing model is OnDemand.
4. 50GB General Purpose SSD (gp2) storage.
5. 30 Baseline IO rate per second, 100 Peak IO rate per second, and 60 Duration of peak IO activity hours per month. | $ 143.51 | 16 | | Application Load Balancer | 1. 100 GB per month for EC2 Instances and IP addresses as targets.
2. Average 40 new connections per second. | $ 25.77 | 17 | | Total | | $ 241.36 | 18 | 19 | 20 | ## Example 2 21 | 22 | Your database instance uses Amazon Aurora MySQL-Compatible, with Multi-AZ deployment option, reserved (1 year) pricing model. The monthly cost of using this solution in the US East (N. Virginia) region is shown below: 23 | 24 | | Service | Dimensions | Cost (MLY) | 25 | | ------- | :--- | ---: | 26 | | AWS Fargate | 1. 2 ECS Tasks that run for 24 hours every day.
2. Each ECS task uses 1 vCPU, 2GB memory, and 20GB ephemeral storage. | $ 72.08 | 27 | | Amazon Aurora MySQL-Compatible | 1. Use db.t3.medium (2 vCPU, 4 GB memory).
2. A Multi-AZ DB cluster has a writer DB instance and a reader DB instances in two separate Availability Zones in the same AWS Region.
3. Pricing model is Reserved (1 year), purchase Option is All Upfront.
4. 50GB General Purpose SSD (gp2) storage, 30 Baseline IO rate per second, 100 Peak IO rate per second, and 60 Duration of peak IO activity hours per month. | $ 103.12 | 28 | | Application Load Balancer | 1. 100 GB per month for EC2 Instances and IP addresses as targets.
2. Average 40 new connections per second. | $ 25.77 | 29 | | Total | | $ 200.97 | 30 | 31 | 32 | ## Example 3 33 | 34 | Your database instance use Amazon Aurora Serverless MySQL-Compatible. The monthly cost of using this solution in the US East (N. Virginia) region is shown below: 35 | 36 | | Service | Dimensions | Cost (MLY) | 37 | | ------- | --- | ---: | 38 | | AWS Fargate | 1. 2 ECS Tasks that run for 24 hours every day.
2. Each ECS task uses 1 vCPU, 2GB memory, and 20GB ephemeral storage. | $ 72.08 | 39 | | Amazon Aurora Serverless MySQL-Compatible | 1. 12 ACUs per day.
2. 50GB database storage.
3. 30 Baseline IO rate per second, 100 Peak IO rate per second, and 60 Duration of peak IO activity hours per month. | $ 67.59 | 40 | | Application Load Balancer | 1. 100 GB per month for EC2 Instances and IP addresses as targets.
2. Average 40 new connections per second. | $ 25.77 | 41 | | Total | | $ 165.44 | 42 | 43 | -------------------------------------------------------------------------------- /docs/en/implementation-guide/revisions.md: -------------------------------------------------------------------------------- 1 | # Revisions 2 | 3 | | Date | Description | 4 | | ------------ | :------------------------------ | 5 | | July 2022 | Release 2.1.0
1. Upgrade Keycloak to version 16.1.1
2. Upgrade aws-cdk to version 1.160.0
3. Upgrade cdk-keycloak to version 0.2.41 | 6 | | Oct 2023 | Release 2.1.6
1. Upgrade Keycloak to version 22.0.4
2. Upgrade aws-cdk to version 2.100.0
3. Upgrade cdk-keycloak to version 2.9.0 | -------------------------------------------------------------------------------- /docs/en/implementation-guide/tutorials/ad-ldap.md: -------------------------------------------------------------------------------- 1 | # Tutorial: How to integrate Keycloak with AD/LDAP? 2 | 3 | Keycloak allows user federation with AD/LDAP. This guide walks you through the user federation with OpenLDAP service. For more information, see [User Federation](https://www.keycloak.org/docs/latest/server_admin/#_user-storage-federation) from the Keycloak documentation. 4 | 5 | Keycloak supports multiple LDAP services including Microsoft AD and OpenLDAP. The following tutorial will run an OpenLDAP service in the same VPC with the Keycloak service. 6 | 7 | ## Prerequisites 8 | 9 | You have already deployed the solution via CloudFormation or AWS CDK and already successfully logged in to the Keycload dashboard as Keycloak admin user. 10 | 11 | ## Steps 12 | 13 | [Step 1. Launch an EC2 instance for OpenLDAP](#step-1-launch-an-ec2-instance-for-openldap) 14 | 15 | [Step 2. Install OpenLDAP](#step-2-install-openldap) 16 | 17 | [Step 3. Create a User Federation on Keycloak](#step-3-create-a-user-federation-on-keycloak) 18 | 19 | [Step 4. Validate the user federation](#step-4-validate-the-user-federation) 20 | 21 | ## Step 1. Launch an EC2 instance for OpenLDAP 22 | 23 | You need to launch an EC2 instance in the same VPC with your Keycloak service, and do the following to configure the security group of this EC2 instance and ensure all traffic from the VPC CIDR can access its LDAP port (TCP 389). 24 | 25 | 1. Log in to the [Amazon EC2 ][Amazon EC2 console] console. 26 | 27 | 2. In the left navigation pane, choose **Security Groups**. 28 | 29 | 3. Enter `KeyCloakKeyCloakContainer` in the Filter box, and click Enter, then copy the **Security group ID**, such as *sg-0121f1140bbfd72c6*. 30 | 31 | 4. Choose the Security Groups where your EC2 instance is located, and add an Inbound rules to allow ECS access to OpenLDAP. 32 | 33 | 5. Choose **Save rules**. 34 | 35 | ## Step 2. Install OpenLDAP 36 | 37 | Install OpenLDAP with Docker in your EC2 instance. 38 | 39 | 1. Connect to your instance. 40 | 2. Do the following: 41 | ``` 42 | # Install docker 43 | yum install -y docker 44 | systemctl start docker 45 | # start the docker container 46 | docker run -p 389:1389 public.ecr.aws/bitnami/openldap:latest 47 | ``` 48 | 49 | 3. Open another terminal and install the OpenLDAP clients. 50 | ``` 51 | # install ldap client 52 | yum install -y openldap-clients 53 | # list all users 54 | ldapsearch -x -b "ou=users,dc=example,dc=org" -H ldap:// 55 | ``` 56 | For example: 57 | ``` 58 | [root@xxxx ~]# ldapsearch -x -b "ou=users,dc=example,dc=org" -H ldap:// 59 | # extended LDIF 60 | # 61 | # LDAPv3 62 | # base with scope subtree 63 | # filter: (objectclass=*) 64 | # requesting: ALL 65 | # 66 | 67 | # users, example.org 68 | dn: ou=users,dc=example,dc=org 69 | objectClass: organizationalUnit 70 | ou: users 71 | 72 | # user01, users, example.org 73 | dn: cn=user01,ou=users,dc=example,dc=org 74 | cn: User1 75 | cn: user01 76 | sn: Bar1 77 | objectClass: inetOrgPerson 78 | objectClass: posixAccount 79 | objectClass: shadowAccount 80 | userPassword:: Yml0bmFtaTE= 81 | uid: user01 82 | uidNumber: 1000 83 | gidNumber: 1000 84 | homeDirectory: /home/user01 85 | 86 | # user02, users, example.org 87 | dn: cn=user02,ou=users,dc=example,dc=org 88 | cn: User2 89 | cn: user02 90 | sn: Bar2 91 | objectClass: inetOrgPerson 92 | objectClass: posixAccount 93 | objectClass: shadowAccount 94 | userPassword:: Yml0bmFtaTI= 95 | uid: user02 96 | uidNumber: 1001 97 | gidNumber: 1001 98 | homeDirectory: /home/user02 99 | 100 | # readers, users, example.org 101 | dn: cn=readers,ou=users,dc=example,dc=org 102 | cn: readers 103 | objectClass: groupOfNames 104 | member: cn=user01,ou=users,dc=example,dc=org 105 | member: cn=user02,ou=users,dc=example,dc=org 106 | 107 | # search result 108 | search: 2 109 | result: 0 Success 110 | 111 | # numResponses: 5 112 | # numEntries: 4 113 | ``` 114 | 115 | Now your default LDAP service is ready. 116 | 117 | ## Step 3. Create a User Federation on Keycloak 118 | 119 | 1. Log in to the Keycloak dashboard as **Keycloak** admin user. 120 | 121 | 2. In the left navigation pane, choose **User Federation**. 122 | 123 | 3. Click the **Add provider** drop-down menu, and choose **ldap**. 124 | 125 | 4. In the page that opens, enter the following information: 126 | 1. **Edit Mode**: Choose `WRITABLE`. 127 | 2. **Vendor**: Choose `Other`. 128 | 3. **Username LDAP attribute**: Enter your LDAP attribute name for username, use `cn` in this tutorial. 129 | 4. **RDN LDAP attribute**: Enter your LDAP attribute name for user RDN, use `cn` in this tutorial. 130 | 5. **UUID LDAP attribute**: Enter your LDAP attribute name for UUID, use `uid` in this tutorial. 131 | 6. **User Object Classes**: Enter your LDAP User Object Classes, use `inetOrgPerson, posixAccount, shadowAccount` in this tutorial. 132 | 7. **Connection URL**: Enter your LDAP connection URL, use `ldap://` in this tutorial, and click **Test connection**, Prompt the following information "Success! LDAP connection successful.". 133 | 8. **Users DN**: Enter your LDAP Users DN, use `ou=users,dc=example,dc=org` in this tutorial. 134 | 9. **Bind Type**: Choose `simple`. 135 | 10. **Bind DN**: Enter your LDAP bind DN, use `cn=admin,dc=example,dc=org` in this tutorial. 136 | 11. **Bind Credential**: Enter your LDAP Bind Credentials, use `adminpassword` in this tutorial, and click `Test authentication`, Prompt the following information "Success! LDAP authentication successful.". 137 | 138 | 5. Choose **Save**. 139 | 140 | 6. Choose **Synchronize all users**. The following information prompts "Success! Sync of users finished successfully. 2 imported users, 0 updated users". 141 | 142 | 7. In the left navigation pane, choose **Users**. 143 | 144 | 8. Choose **View all users**, **user1** and **user2** should be imported successfully. 145 | 146 | ## Step 4. Validate the User federation 147 | 148 | Now you can validate the User Federation with the **account-console** login. 149 | 150 | 1. Log in to the Keycloak dashboard as **Keycloak** admin user. 151 | 152 | 2. In the left navigation pane, choose **Clients**. 153 | 154 | 3. Click the **Base URL** of account-console. 155 | 156 | 4. On the Keycloak account console you have been redirected, click **Sign In** in the upper right corner. 157 | 158 | 5. Enter **user1** to Username or email, enter **bitnami1** to Password. 159 | 160 | 6. Click **Sign In** to login to the console. 161 | 162 | ## FAQ 163 | 164 | **1. Does Keycloak support LDAPS protocol?** 165 | 166 | Yes. both ldap:// and ldaps:// are supported. To enable ldaps://, make sure your AD/LDAP is running with LDAPS and has properly imported the certificate. 167 | 168 | **2. What vendor type should I select if I am running Microsoft AD server?** 169 | 170 | Select Active Directory from the Vendor list. 171 | 172 | ![07-en-keycloak-user-federation-provider](../../images/implementation-guide/tutorial/ad-ldap/07-en-keycloak-user-federation-provider.png) 173 | 174 | 175 | [Amazon Certificate Manager]: https://aws.amazon.com/cn/certificate-manager/ 176 | [AWS Certificate Manager console]: https://console.aws.amazon.com/acm/home 177 | [AWS CloudFormation console]: https://console.aws.amazon.com/cloudformation/home 178 | [Amazon EC2 console]: https://console.aws.amazon.com/ec2 179 | [AWS Secrets Manager console]: https://console.aws.amazon.com/secretsmanager 180 | [Amazon Route 53 console]: https://console.aws.amazon.com/route53 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/en/implementation-guide/tutorials/api-gateway.md: -------------------------------------------------------------------------------- 1 | # Tutorial: How to integrate Keycloak with Amazon API Gateway? 2 | 3 | This tutorial demonstrates how to control permissions for different users to access different API interfaces through Keycloak. For more information, see [Authorization Services](https://www.keycloak.org/docs/16.1/authorization_services/index.html) from the Keycloak document. 4 | 5 | ## Architecture 6 | ![01-en-architecture-diagram](../../images/implementation-guide/tutorial/api-gateway/01-en-architecture-diagram.svg) 7 | 8 | ## Prerequisites 9 | 10 | 1. You have already deployed the keycloak-on-aws via CloudFormation or AWS CDK and already successfully logged in the Keycload dashboard as Keycloak admin user. 11 | 12 | 2. Make sure your have the following JAVA_OPTS filled in CloudFormation parameter. 13 | ``` 14 | -Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.profile.feature.upload_scripts=enabled 15 | ``` 16 | 17 | ## Deployment Overview 18 | 19 | Use the following steps to deploy this solution on AWS. 20 | 21 | [Step 1. Git clone keycloak-on-aws](#step-1-git-clone-keycloak-on-aws) 22 | 23 | [Step 2. Import the Keycloak example config](#step-2-import-the-keycloak-example-config) 24 | 25 | [Step 3. Run serverless-express-auth locally](#step-3-run-serverless-express-auth-locally) 26 | 27 | [Step 4. Run Vue UI locally](#step-4-run-vue-ui-locally) 28 | 29 | [Step 5. Validate the User Permissions](#step-5-validate-the-user-permissions) 30 | 31 | ## Step 1. Git clone keycloak-on-aws 32 | 33 | Download the keycloak-on-aws code locally. 34 | 35 | ``` 36 | git clone https://github.com/aws-samples/keycloak-on-aws.git 37 | cd keycloak-on-aws 38 | ``` 39 | 40 | ## Step 2. Import the Keycloak example config 41 | 42 | 1. Login the Keycloak dashboard as **keycloak** admin user. 43 | 44 | 2. Hover over the **Master** in the left navigation pane, and choose **Add realm**. 45 | 46 | 3. Choose **Select file**, and select **tutorials/api-gateway/resoures/keycloak.json** in your git downloaded code. 47 | ![02-en-keycloak-add-realm](../../images/implementation-guide/tutorial/api-gateway/02-en-keycloak-add-realm.png) 48 | 49 | 5. Choose **Create**. 50 | 51 | ## Step 3. Run serverless-express-auth locally 52 | 53 | This example is mainly based on [expressjs](https://github.com/expressjs/express) and official [keycloak-nodejs-connect](https://github.com/keycloak/keycloak-nodejs-connect). And using [serverless-express](https://github.com/vendia/serverless-express/) to make expressjs be able to run on lambda. 54 | 55 | The advantage of this way is keycloak-nodejs-connect is maintained by keycloak team and it is the recommended way to connect to keycloak. 56 | ``` 57 | Notice: The document of keycloak-nodejs-connect is at https://www.keycloak.org/docs/latest/securing_apps/#_nodejs_adapter 58 | ``` 59 | 60 | 1. Login the Keycloak dashboard as **keycloak** admin user. 61 | 62 | 2. In the left navigation pane, and choose **Clients**. 63 | 64 | 3. Choose **vue** to show detailed metadata. 65 | 66 | 4. Choose **Installation**. 67 | 68 | 5. Click **Format Option**, choose **Keycloak OIDC JSON**. 69 | 70 | 6. According to keycloak client installation config to update **tutorials/api-gateway/resources/keycloak.json** in your git downloaded code. 71 | ``` 72 | { 73 | "realm": "keycloak-on-aws", 74 | "auth-server-url": "https://keycloak.yourdomain.com/auth/", 75 | "ssl-required": "external", 76 | "resource": "vue", 77 | "public-client": true, 78 | "confidential-port": 0, 79 | // Don't remove the following line, this is for vue-ui to contact to api gw !!! 80 | "x-api-gw-url": "http://localhost:3003/dev/hello" 81 | } 82 | ``` 83 | 7. Go to **tutorials/api-gateway/serverless-express-auth** directory, do the following. 84 | ``` 85 | cd tutorials/api-gateway/serverless-express-auth 86 | yarn 87 | yarn offline 88 | ``` 89 | Sample Output 90 | ``` 91 | yarn run v1.22.19 92 | $ SLS_DEBUG=* serverless offline --host 0.0.0.0 --httpPort 3003 --lambdaPort=3019 93 | ... 94 | offline: Offline [http for lambda] listening on http://0.0.0.0:3019 95 | offline: Function names exposed for local invocation by aws-sdk: 96 | * authEndpoint: serverless-express-auth-dev-authEndpoint 97 | * hello: serverless-express-auth-dev-hello 98 | [offline] Lambda Invocation Routes (for AWS SDK or AWS CLI): 99 | * POST http://0.0.0.0:3019/2015-03-31/functions/serverless-express-auth-dev-authEndpoint/invocations 100 | * POST http://0.0.0.0:3019/2015-03-31/functions/serverless-express-auth-dev-hello/invocations 101 | [offline] Lambda Async Invocation Routes (for AWS SDK or AWS CLI): 102 | * POST http://0.0.0.0:3019/2014-11-13/functions/serverless-express-auth-dev-authEndpoint/invoke-async/ 103 | * POST http://0.0.0.0:3019/2014-11-13/functions/serverless-express-auth-dev-hello/invoke-async/ 104 | offline: Configuring Authorization: hello authEndpoint 105 | [offline] Creating Authorization scheme for hello-authEndpoint-GET-hello 106 | 107 | ┌───────────────────────────────────────────────────────────────────────┐ 108 | │ │ 109 | │ GET | http://0.0.0.0:3003/dev/hello │ 110 | │ POST | http://0.0.0.0:3003/2015-03-31/functions/hello/invocations │ 111 | │ │ 112 | └───────────────────────────────────────────────────────────────────────┘ 113 | 114 | offline: [HTTP] server ready: http://0.0.0.0:3003 🚀 115 | offline: 116 | offline: Enter "rp" to replay the last request 117 | ``` 118 | 119 | 159 | 160 | ## Step 4. Run Vue UI locally 161 | 162 | ``` 163 | cd tutorials/api-gateway/vue-ui 164 | yarn 165 | yarn serve 166 | ``` 167 | Sample Output 168 | ``` 169 | yarn run v1.22.19 170 | $ vue-cli-service serve 171 | Browserslist: caniuse-lite is outdated. Please run: 172 | npx browserslist@latest --update-db 173 | Why you should do it regularly: https://github.com/browserslist/browserslist#browsers-data-updating 174 | INFO Starting development server... 175 | Starting type checking service... 176 | Using 1 worker with 2048MB memory limit 177 | 98% after emitting CopyPlugin 178 | 179 | DONE Compiled successfully in 1638ms 3:42:04 PM 180 | 181 | No type errors found 182 | Version: typescript 4.2.4 183 | Time: 1111ms 184 | 185 | App running at: 186 | - Local: http://localhost:8080/ 187 | - Network: http://localhost:8080/ 188 | 189 | Note that the development build is not optimized. 190 | To create a production build, run yarn build. 191 | ``` 192 | 193 | ## Step 5. Validate the User Permissions 194 | 195 | A common scenario is, different users have different permissions to perform an action (allow/deny). We have built in two different users, user1 is allowed to call API GW while user2 is not. 196 | 197 | Users: 198 | 199 | |user|password|realm role|desription| 200 | |---|---|---|:---| 201 | |user1|user1|call-api|user1 is permited to call api gateway| 202 | |user2|user2|-|user2 is not permited to call api gateway| 203 | 204 | 1. Log in to the **Vue UI** console, such as *http://localhost:8080/*. 205 | 206 | 2. Choose **Login**. 207 | 208 | 3. Enter **user1** to Username or email, enter **user1** to Password. 209 | 210 | 4. Choose **Sign In**. 211 | 212 | 5. Choose **Request**, you will get the following response message, the response will be successfully. 213 | ``` 214 | { 215 | "url": "http://localhost:3003/dev/hello", 216 | "status": 200, 217 | "statusText": "OK", 218 | "data": { 219 | "message": "Hello World from protect server" 220 | } 221 | } 222 | ``` 223 | 6. Choose **Logout**. 224 | 225 | 7. Enter **user2** to Username or email, enter **user2** to Password. 226 | 227 | 8. Choose **Sign In**. 228 | 229 | 9. Choose **Request**, you will get the following response message, the response will fail. 230 | ``` 231 | { 232 | "url": "http://localhost:3003/dev/hello", 233 | "status": 401, 234 | "statusText": "Unauthorized", 235 | "data": { 236 | "statusCode": 401, 237 | "error": "Unauthorized", 238 | "message": "Unauthorized" 239 | } 240 | } 241 | ``` 242 | 243 | ## FAQ 244 | 245 | **1. How to export Keycloak realm users?** 246 | 247 | Run the following command to export Keycloak realm users: 248 | 249 | ``` 250 | $ docker exec 251 | $ /opt/jboss/keycloak/bin/standalone.sh -Dkeycloak.migration.action=export -Dkeycloak.migration.realmName=keycloak-on-aws -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.file=realm-export.json -Djboss.socket.binding.port-offset=99 252 | ``` 253 | 254 | For more information, refer to . 255 | 256 | [Amazon Certificate Manager]: https://aws.amazon.com/cn/certificate-manager/ 257 | [AWS Certificate Manager console]: https://console.aws.amazon.com/acm/home 258 | [AWS CloudFormation console]: https://console.aws.amazon.com/cloudformation/home 259 | [Amazon EC2 console]: https://console.aws.amazon.com/ec2 260 | [AWS Secrets Manager console]: https://console.aws.amazon.com/secretsmanager 261 | [Amazon Route 53 console]: https://console.aws.amazon.com/route53 262 | 263 | 264 | -------------------------------------------------------------------------------- /docs/en/index.md: -------------------------------------------------------------------------------- 1 | This solution allows you to quickly deploy a Keycloak cluster on Amazon Web Services Cloud. Keycloak is an Open Source Identity and Access Management solution for modern Applications and Services. Providing a customizable user interface, Keycloak supports use cases such as Single Sign-On (SSO), user registration, and user federation. You can configure Keycloak to integrate with Active Directory and LDAP. You can also set up Keycloak to delegate authentication to third-party identity providers. 2 | 3 | This solution mainly includes the following features: 4 | 5 | - **Single-Sign On**: supports standard protocols such as OpenID Connect, OAuth 2.0, and SAML 2.0. 6 | 7 | - **Identity and Access Management**: provides user federation, strong authentication, user management, fine-grained authorization, and so on. Authentication can be added to applications and services without having to deal with storing users or authenticating users. 8 | 9 | ## Terminology 10 | 11 | The following table lists the terminologies related to this solution. 12 | 13 | | Terminology | Full Name | Description | 14 | | --- | --- | :--- | 15 | | SSO | Single-Sign On | Single sign-on (SSO) is an authentication scheme that allows a user to log in with a single ID to any of several related, yet independent, software systems. | 16 | SAML | Security Assertion Markup Language | Security Assertion Markup Language is an open standard for exchanging authentication and authorization data between parties, in particular, between an identity provider and a service provider. SAML is an XML-based markup language for security assertions (statements that service providers use to make access-control decisions). | 17 | OAuth | Open Authorization | Open Authorization is an open standard for access delegation, commonly used as a way for internet users to grant websites or applications access to their information on other websites but without giving them the passwords. | 18 | OpenID | OpenID | OpenID is an open standard and decentralized authentication protocol promoted by the non-profit OpenID Foundation. For more information, refer to [OpenID Connect](https://en.wikipedia.org/wiki/OpenID#OpenID_Connect_(OIDC)). | 19 | ICP | Internet Content Provider | ICP license is a permit issued by the Chinese Ministry of Industry and Information Technology (MIIT) to permit China-based websites to operate in China. | 20 | JWT | JSON Web Token | JSON Web Token is a proposed Internet standard for creating data with optional signature and/or optional encryption whose payload holds JSON that asserts some number of claims. The tokens are signed either using a private secret or a public/private key. | 21 | LDAP | Lightweight Directory Access Protocol | The Lightweight Directory Access Protocol is an open, vendor-neutral, industry standard application protocol for accessing and maintaining distributed directory information services over an Internet Protocol (IP) network. | 22 | AD | Active Directory | Active Directory is a directory service developed by Microsoft for Windows domain networks. It is included in most Windows Server operating systems as a set of processes and services. | 23 | 24 | This implementation guide describes architectural considerations and configuration steps for deploying the Keycloak solution in the AWS cloud. It includes links to [CloudFormation][cloudformation] templates that launches and configures the AWS services required to deploy this solution using AWS best practices for security and availability. 25 | 26 | The guide is intended for IT architects, developers, DevOps with practical experience architecting in the AWS Cloud. 27 | 28 | [cloudformation]: https://aws.amazon.com/en/cloudformation/ -------------------------------------------------------------------------------- /docs/images/architecture/01-keycloak-on-aws-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/architecture/01-keycloak-on-aws-architecture.png -------------------------------------------------------------------------------- /docs/images/aws-solutions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/aws-solutions.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/deployment/16-en-keycloak-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/deployment/16-en-keycloak-index.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/deployment/17-en-keycloak-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/deployment/17-en-keycloak-login.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/01-en-ec2-keycloak-security-group-id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/01-en-ec2-keycloak-security-group-id.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/02-en-ec2-add-security-group-rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/02-en-ec2-add-security-group-rules.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/03-en-keycloak-add-user-provider-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/03-en-keycloak-add-user-provider-01.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/04-en-keycloak-manger-users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/04-en-keycloak-manger-users.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/05-en-keycloak-clients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/05-en-keycloak-clients.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/06-en-keycloak-account-console-signin-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/06-en-keycloak-account-console-signin-01.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/06-en-keycloak-account-console-signin-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/06-en-keycloak-account-console-signin-02.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/06-en-keycloak-account-console-signin-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/06-en-keycloak-account-console-signin-03.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/ad-ldap/07-en-keycloak-user-federation-provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/ad-ldap/07-en-keycloak-user-federation-provider.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/api-gateway/02-en-keycloak-add-realm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/api-gateway/02-en-keycloak-add-realm.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/api-gateway/03-en-keycloak-validate-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/api-gateway/03-en-keycloak-validate-01.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/api-gateway/03-en-keycloak-validate-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/api-gateway/03-en-keycloak-validate-02.png -------------------------------------------------------------------------------- /docs/images/implementation-guide/tutorial/api-gateway/03-en-keycloak-validate-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/docs/images/implementation-guide/tutorial/api-gateway/03-en-keycloak-validate-03.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | <<<<<<< HEAD 9 | 10 | ======= 11 | 12 | >>>>>>> v2.1.0-alpha 13 | -------------------------------------------------------------------------------- /docs/mkdocs.base.yml: -------------------------------------------------------------------------------- 1 | site_name: Keycloak on AWS 2 | site_url: https://aws-samples.github.io/keycloak-on-aws/ 3 | repo_url: https://github.com/aws-samples/keycloak-on-aws 4 | repo_name: aws-samples/keycloak-on-aws 5 | 6 | theme: 7 | name: material 8 | features: 9 | - navigation.tabs 10 | favicon: images/aws-solutions.png 11 | logo: images/aws-solutions.png 12 | icon: 13 | repo: fontawesome/brands/github 14 | language: en 15 | 16 | extra: 17 | generator: false 18 | copyright: Copyright © 2020 - 2022 Amazon Web Services 19 | alternate: 20 | - name: English 21 | link: /keycloak-on-aws/en/ 22 | lang: en 23 | - name: 简体中文 24 | link: /keycloak-on-aws/zh/ 25 | lang: zh 26 | bucket: aws-gcr-solutions 27 | version: v2.1.0 28 | 29 | plugins: 30 | - search 31 | - include-markdown 32 | - macros 33 | 34 | markdown_extensions: 35 | - admonition 36 | - attr_list 37 | - pymdownx.details 38 | - pymdownx.superfences 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/mkdocs.en.yml: -------------------------------------------------------------------------------- 1 | INHERIT: ./mkdocs.base.yml 2 | docs_dir: en 3 | site_dir: ./site/en 4 | theme: 5 | language: en 6 | features: 7 | - navigation.expand 8 | nav: 9 | - Implementation Guide: 10 | - Overview: index.md 11 | - Cost: implementation-guide/cost.md 12 | - Architecture overview: implementation-guide/architecture.md 13 | - Considerations: implementation-guide/considerations.md 14 | - Deployment: implementation-guide/deployment.md 15 | - Tutorials: 16 | - How to integrate Keycloak with Amazon API Gateway?: implementation-guide/tutorials/api-gateway.md 17 | - How to integrate Keycloak with AD/LDAP?: implementation-guide/tutorials/ad-ldap.md 18 | - Additional resources: implementation-guide/additional-resources.md 19 | - Revisions: implementation-guide/revisions.md 20 | -------------------------------------------------------------------------------- /docs/mkdocs.zh.yml: -------------------------------------------------------------------------------- 1 | INHERIT: ./mkdocs.base.yml 2 | docs_dir: zh 3 | site_dir: ./site/zh 4 | theme: 5 | language: zh 6 | features: 7 | - navigation.expand 8 | nav: 9 | - 实施指南: 10 | - 概述: index.md 11 | - 成本预估: implementation-guide/cost.md 12 | - 架构概览: implementation-guide/architecture.md 13 | - 区域支持: implementation-guide/considerations.md 14 | - 部署解决方案: implementation-guide/deployment.md 15 | - 教程: 16 | - 如何将 Keycloak 与 Amazon API Gateway 集成?: implementation-guide/tutorials/api-gateway.md 17 | - 如何将 Keycloak 与 AD/LDAP 集成?: implementation-guide/tutorials/ad-ldap.md 18 | - 更多资源: implementation-guide/additional-resources.md 19 | - 版本发布说明: implementation-guide/revisions.md 20 | 21 | -------------------------------------------------------------------------------- /docs/zh/images: -------------------------------------------------------------------------------- 1 | ../images -------------------------------------------------------------------------------- /docs/zh/implementation-guide/additional-resources.md: -------------------------------------------------------------------------------- 1 | # AWS Services 2 | 3 | * [AWS CloudFormation](https://amazonaws.cn/cloudformation/) 4 | * [Amazon ECS](https://amazonaws.cn/ecs/) 5 | * [Amazon ECR](https://amazonaws.cn/ecr/) 6 | * [Amazon Certificate Manager (ACM)](https://amazonaws.cn/certificate-manager/) 7 | * [AWS Identity and Access Management (IAM)](https://amazonaws.cn/iam/) 8 | * [Amazon Route 53](https://amazonaws.cn/route53/) 9 | * [Amazon Aurora](https://amazonaws.cn/rds/aurora) 10 | * [AWS Secrets Manager](https://amazonaws.cn/secrets-manager/) 11 | * [Application Load Balancer](https://amazonaws.cn/elasticloadbalancing/application-load-balancer/) 12 | * [AWS Fargate](https://amazonaws.cn/fargate/) 13 | -------------------------------------------------------------------------------- /docs/zh/implementation-guide/architecture.md: -------------------------------------------------------------------------------- 1 | 使用默认参数部署此解决方案会在 AWS 云中构建以下环境。 2 | 3 | ![architecture](../images/architecture/01-keycloak-on-aws-architecture.png) 4 | 5 | 图1: 解决方案架构图 6 | 7 | 此解决方案在您的 AWS 云账户中部署 AWS CloudFormation 模板并完成以下设置。 8 | 9 | - 在两个 [可用区][Availability Zones] 部署的高可用架构; 10 | - 根据 AWS 最佳实践在 [Amazon Virtual Private Cloud (Amazon VPC)][Amazon VPC] 中配置了公有和私有子网进行资源隔离; 11 | - 处在私有子网中的资源通过公有子网中的NAT网关连接到互联网,但不能接收来自互联网的未经请求的入站连接; 12 | - 在私有子网中: 13 | - 使用 [AWS Fargate][AWS Fargate] 运行和扩展 [Amazon ECS][Amazon ECS] 容器工作负载; 14 | - 使用 [Application Load Balancer][Application Load Balancer] 负载均衡请求流量; 15 | - 部署 [Amazon Aurora Serverless MySQL-Compatible][Amazon Aurora Serverless] 或 [Amazon Aurora MySQL-Compatible][Amazon Aurora] 数据库集群。 16 | - 为 [Amazon ECS][Amazon ECS] 服务创建 [IAM][AWS Identity and Access Management] 角色; 17 | - 通过 [AWS Secrets Manager][AWS Secrets Manager] 管理 [Keycloak][Keycloak] 控制台登录和数据库连接密钥; 18 | - 通过 [AWS Certificate Manager (ACM)][Amazon Certificate Manager] 将现有的证书应用到 [Application Load Balancer][Application Load Balancer] 的域名上; 19 | - 在 [Amazon Route 53][Amazon Route 53] 中添加别名记录,用于访问Keycloak控制台。 20 | 21 | [Availability Zones]: https://aws.amazon.com/about-aws/global-infrastructure/regions_az/ 22 | [AWS CloudFormation]: https://amazonaws.cn/cloudformation/ 23 | [Amazon VPC]: https://amazonaws.cn/vpc/ 24 | [AWS Fargate]: https://amazonaws.cn/fargate/ 25 | [Amazon ECS]: https://amazonaws.cn/ecs/ 26 | [Amazon ECR]: https://amazonaws.cn/ecr/ 27 | [Application Load Balancer]: https://amazonaws.cn/elasticloadbalancing/application-load-balancer/ 28 | [Amazon Certificate Manager]: https://amazonaws.cn/certificate-manager/ 29 | [AWS Identity and Access Management]: https://amazonaws.cn/iam/ 30 | [Amazon Route 53]: https://amazonaws.cn/route53/ 31 | [Amazon Aurora]: https://amazonaws.cn/rds/aurora 32 | [Amazon Aurora Serverless]: https://amazonaws.cn/rds/aurora/serverless/ 33 | [AWS Secrets Manager]: https://amazonaws.cn/secrets-manager/ 34 | [Keycloak]: https://www.keycloak.org/ 35 | -------------------------------------------------------------------------------- /docs/zh/implementation-guide/considerations.md: -------------------------------------------------------------------------------- 1 | 2 | 目前本解决方案使用的服务可能并非在所有亚马逊云科技区域都可用。您需要在提供所需服务的亚马逊云科技区域启动此解决方案。有关最新的区域可用性信息,请参阅 [AWS 区域服务列表][services]。 3 | 4 | 因为方案数据库有Amazon Aurora MySQL-Compatible,Amazon Aurora Serverless v1 MySQL-Compatible和Amazon Aurora Serverless v2 MySQL-Compatible三种选择,所以使用CloudFormation部署时,需要注意区域是否支持您的选择。 5 | 6 | 对于 Aurora Serverless 部署,CloudFormation 模板中默认使用 Aurora Serverless v2 MySQL-Compatible。 Aurora Serverless v2 可以更快、更精细地扩展,并且与其他 Aurora 功能(例如 Reader 数据库实例)具有更好的兼容性。 有关更多信息,请参阅[Aurora Serverless v2 和 Aurora Serverless v1 的比较][comparisons]。 7 | 8 | **支持部署的全球区域** 9 | 10 | | 区域ID | 区域名称 | Amazon Aurora MySQL-Compatible | Amazon Aurora Serverless v1 MySQL-Compatible |Amazon Aurora Serverless v2 MySQL-Compatible | 11 | | -------------- | ------------------------ | :------------: | :----------------------: | :----------------------: | 12 | | us-east-1 | US East (N. Virginia) | ✔ | ✔ | ✔ | 13 | | us-east-2 | US East (Ohio) | ✔ | ✔ | ✔ | 14 | | us-west-1 | US West (N. California) | ✔ | ✔ | ✔ | 15 | | us-west-2 | US West (Oregon) | ✔ | ✔ | ✔ | 16 | | af-south-1 | Africa (Cape Town) | ✔ | | ✔ | 17 | | ap-east-1 | Asia Pacific (Hongkong) | ✔ | | ✔ | 18 | | ap-south-2 | Asia Pacific (Hyderabad) | ✔ | | ✔ | 19 | | ap-southeast-3 | Asia Pacific (Jakarta) | ✔ | | ✔ | 20 | | ap-southeast-4 | Asia Pacific (Melbourne) | ✔ | | ✔ | 21 | | ap-south-1 | Asia Pacific (Mumbai) | ✔ | ✔ | ✔ | 22 | | ap-northeast-3 | Asia Pacific (Osaka) | ✔ | | ✔ | 23 | | ap-northeast-2 | Asia Pacific (Seoul) | ✔ | ✔ | ✔ | 24 | | ap-southeast-1 | Asia Pacific (Singapore) | ✔ | ✔ | ✔ | 25 | | ap-southeast-2 | Asia Pacific (Sydney) | ✔ | ✔ | ✔ | 26 | | ap-northeast-1 | Asia Pacific (Tokyo) | ✔ | ✔ | ✔ | 27 | | ca-central-1 | Canada (Central) | ✔ | ✔ | ✔ | 28 | | eu-central-1 | Europe (Frankfurt) | ✔ | ✔ | ✔ | 29 | | eu-west-1 | Europe (Ireland) | ✔ | ✔ | ✔ | 30 | | eu-west-2 | Europe (London) | ✔ | ✔ | ✔ | 31 | | eu-south-1 | Europe (Milan) | ✔ | | ✔ | 32 | | eu-west-3 | Europe (Paris) | ✔ | ✔ | ✔ | 33 | | eu-south-2 | Europe (Spain) | ✔ | | ✔ | 34 | | eu-north-1 | Europe (Stockholm) | ✔ | | ✔ | 35 | | eu-central-2 | Europe (Zurich) | ✔ | | ✔ | 36 | | il-central-1 | Israel (Tel Aviv) | ✔ | | ✔ | 37 | | me-south-1 | Middle East (Bahrain) | ✔ | | ✔ | 38 | | me-central-1 | Middle East (UAE) | ✔ | | ✔ | 39 | | sa-east-1 | South America (Sao Paulo)| ✔ | | ✔ | 40 | 41 | **支持部署的中国区域** 42 | 43 | | 区域ID | 区域名称 | Amazon Aurora MySQL-Compatible | Amazon Aurora Serverless v1 MySQL-Compatible |Amazon Aurora Serverless v2 MySQL-Compatible | 44 | | -------------- | ----------------------------------------- | :------------: | :----------------------: | :----------------------: | 45 | | cn-north-1 | China (Beijing) Region Operated by Sinnet | ✔ | | ✔ | 46 | | cn-northwest-1 | China (Ningxia) Region Operated by NWCD | ✔ | ✔ | ✔ | 47 | 48 | 49 | [services]: https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/?nc1=h_ls 50 | [comparisons]: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html#Serverless.v1-v2-requirements -------------------------------------------------------------------------------- /docs/zh/implementation-guide/cost.md: -------------------------------------------------------------------------------- 1 | 您需要承担运行解决方案时使用亚马逊云科技各个服务的成本费用。 截至2022年7月,影响解决方案成本的主要因素包括: 2 | 3 | - AWS Fargate 4 | - Amazon Aurora MySQL-Compatible 5 | - Application Load Balancer 6 | 7 | ## 示例 1 8 | 9 | 假设您的数据库是 Amazon Aurora MySQL-Compatible,使用多可用区集群部署、按需使用计价模式,在美国东部(弗吉尼亚北部)区域(us-east-1)使用此解决方案每月的成本如下所示: 10 | 11 | | 服务 | 用量 | 成本/月 | 12 | | ------- | --- | ---: | 13 | | AWS Fargate | 1. 2 个 ECS 任务每天运行 24 小时;
2. 每个 ECS 任务使用 1 vCPU、2 GB 内存、20 GB 的临时存储;| $ 72.08 | 14 | | Amazon Aurora MySQL-Compatible | 1. 实例类型为 db.t3.medium (2 vCPU, 4 GB 内存);
2. 在同一区域下的两个可用区中部署具有一个写入器数据库实例和一个读取器数据库实例的数据库集群;
3. 按需使用计价模式;
4. 配置 50 GB General Purpose SSD (gp2) 存储;
5. 每秒 30 的基线 IO 速率、每秒 100 的峰值 IO 速率、每月高峰 IO 活动持续时间为60个小时;| $ 143.51 | 15 | | Application Load Balancer | 1. 每月 100 GB 将 EC2 作为目标的流量;
2. 平均每秒 40 个新连接; | $ 25.77 | 16 | |总计 | | $ 241.36 | 17 | 18 | 19 | ## 示例 2 20 | 21 | 假设您的数据库是 Amazon Aurora MySQL-Compatible,使用多可用区集群部署、一年预留实例的计价模式,在美国东部(弗吉尼亚北部)区域(us-east-1)使用此解决方案的成本如下所示: 22 | 23 | | 服务 | 用量 | 成本/月 | 24 | | ------- | --- | ---: | 25 | | AWS Fargate | 1. 2 个 ECS 任务每天运行 24 小时;
2. 每个 ECS 任务使用 1 vCPU、2 GB 内存、20 GB 的临时存储;| $ 72.08 | 26 | | Amazon Aurora MySQL-Compatible | 1. 实例类型为 db.t3.medium (2 vCPU, 4 GB 内存);
2. 在同一区域下的两个可用区中部署具有一个写入器数据库实例和一个读取器数据库实例的数据库集群;
3. 一年预留实例的计价模式,购买选项为预付全部费用;
4. 配置 50 GB General Purpose SSD (gp2) 存储;
5. 每秒 30 的基线 IO 速率、每秒 100 的峰值 IO 速率、每月高峰 IO 活动持续时间为60个小时; | $ 103.12 | 27 | | Application Load Balancer | 1. 每月 100 GB 将 EC2 作为目标的流量;
2. 平均每秒 40 个新连接; | $ 25.77 | 28 | |总计 | | $ 200.97 | 29 | 30 | ## 示例 3 31 | 32 | 假设您的数据库是 Amazon Aurora Serverless MySQL-Compatible,在美国东部(弗吉尼亚北部)区域(us-east-1)使用此解决方案的成本如下所示: 33 | 34 | | 服务 | 用量 | 成本/月 | 35 | | ------- | --- | ---: | 36 | | AWS Fargate | 1. 2 个 ECS 任务每天运行 24 小时;
2. 每个 ECS 任务使用 1 vCPU、2 GB 内存、20 GB 的临时存储;| $ 72.08 | 37 | | Amazon Aurora Serverless MySQL-Compatible | 1. 每天使用 12 ACUs;
2. 50 GB 的数据库存储;
3. 每秒 30 的基线 IO 速率、每秒 100 的峰值 IO 速率、每月高峰 IO 活动持续时间为60个小时; | $ 67.59 | 38 | | Application Load Balancer | 1. 每月 100 GB 将 EC2 作为目标的流量;
2. 平均每秒 40 个新连接; | $ 25.77 | 39 | |总计 | | $ 165.44| 40 | 41 | -------------------------------------------------------------------------------- /docs/zh/implementation-guide/deployment.md: -------------------------------------------------------------------------------- 1 | 在部署解决方案之前,建议您先查看本指南中有关架构图和区域支持等信息,然后按照下面的说明配置解决方案并将其部署到您的帐户中。 2 | 3 | **部署时间**:大约 30 分钟 4 | 5 | 6 | ## 前提条件 7 | 8 | 确保您要部署解决方案的目标区域满足以下条件: 9 | 10 | - 若部署在中国区,需要已有被 ICP 备案的域名并用于申请 ACM 证书。 11 | - 域名的证书在 ACM 中创建,并通过域名进行验证。 12 | - 具有 4 个子网(包括两个公有子网和两个私有子网)和 NAT 网关的 VPC。 13 | - 在 [AWS 服务](./additional-resources.md) 中所列出的所有AWS服务均可用。 14 | 15 | 16 | ## 部署概述 17 | 18 | 使用以下步骤在 AWS 上部署此解决方案: 19 | 20 | 步骤 1. 创建 ACM 证书 21 | 22 | 步骤 2. 验证域所有权 23 | 24 | 步骤 3. 启动堆栈 25 | 26 | 步骤 4. 在 Route 53 中创建记录以解析域名 27 | 28 | 步骤 5. 访问 Keycloak Web 控制台 29 | 30 | ## 步骤 1. 创建 ACM 证书 31 | 32 | 为了确保 Keycloak 可以连接到 Cognito 身份池,需要确保 Keycloak 提供 HTTPS 服务。这意味着必须使用 ACM 证书或第三方证书。有关如何使用的详细信息,请参阅[如何上载 SSL 证书并将其导入 AWS Identity and Access Management (IAM)](https://aws.amazon.com/cn/premiumsupport/knowledge-center/import-ssl-certificate-to-iam/)。 33 | 34 | 本指南以 AWS Certificate Manager (ACM) 的使用为例。有关 ACM 的更多信息,请参阅 [Amazon Certificate Manager][Amazon Certificate Manager]。 35 | 36 | 1. 登录 [AWS Certificate Manager][AWS Certificate Manager console] 控制台。 37 | 38 | 2. 在顶部的导航栏中,选择 Keycloak 想要部署的**区域**。 39 | 40 | 3. 在左侧的导航窗中,选择**列出证书**。 41 | 42 | 4. 选择**请求**。 43 | 44 | 5. 如果您选择从全球区域部署解决方案,为**证书类型**选择**请求公有证书**。如果您选择从中国区域部署解决方案,仅有一个选项**请求公有证书**,选择该选项。然后选择**下一步**。 45 | 46 | 6. 选择**下一步**。 47 | 48 | 7. 在**请求公有证书**页面,完成以下配置: 49 | 1. 在**域名**中输入您的 Keycloak 的域名,例如 *keycloak.yourdomain.com*。 50 | 2. 修改 **选择验证方法** 为 **DNS 验证 - 推荐**。 51 | 52 | 8. 选择**请求**。 53 | 54 | 9. 查看**证书**列表,新申请的证书 ID 的**状态**处于**等待验证**。 55 | 56 | 10. 点击新申请的**证书 ID** 查看详细信息。在新打开的页面中记录以下内容: 57 | 1. **状态**中的 **ARN**。 58 | 2. **域**中的 **CNAME 名称**。 59 | 3. **域**中的 **CNAME 值**。 60 | 61 | ## 步骤 2. 验证域所有权 62 | 63 | > **_NOTE:_** 以下内容为通过 Route 53 验证域名所有权。如果您使用其他域名服务器 (DNS),请参考 DNS 提供商文档来验证您的域名所有权。 64 | 65 | 将 CNAME 记录添加到 Route 53 以验证域名是否归您所有并可供您使用。如果未创建托管区域,请参考[将 Amazon Route 53 配置为 DNS 服务][Configuring Amazon Route 53 as your DNS service]。 66 | 67 | 1. 登录 [Amazon Route 53][Amazon Route 53 console] 控制台。 68 | 69 | 2. 在左侧的导航窗中,选择**托管区域**。 70 | 71 | 3. 点击**域名**查看详细信息,在新打开的页面中点击**创建记录** ,并完成以下操作: 72 | 1. 在**记录名称**中输入步骤 1. 创建 ACM 证书 中记录的 **CNAME name**,例如 *_5db1a2688389b3b76ef6e2accaf9a85d.keycloak.yourdomain.com*。 73 | 2. 修改**记录类型**为 **CNAME**。 74 | 3. 在 **值** 中输入 步骤 1. 创建 ACM 证书 中记录的 **CNAME value** ,例如 *_1e9108227615sd40f3a767a9dc7a29cb.bpxxncpwjz.acm-validations.aws*。 75 | 76 | 4. 选择**创建记录**。 77 | 78 | 5. 返回到 [AWS Certificate Manager][AWS Certificate Manager console] 控制台并等待大约5分钟,点击**刷新** 按钮,直到 ACM 证书的**状态**变为**已颁发**。 79 | 80 | ## 步骤 3. 启动堆栈 81 | 82 | 为满足不同用户的需求,共有 4 种部署方式供您选择。 83 | 84 | 对于 Aurora Serverless 选项,CloudFormation 模板中默认使用 Aurora Serverless v2 MySQL-Compatible。有关更多信息,请参阅[Aurora Serverless v2 和 Aurora Serverless v1 的比较][comparisons]。 85 | 86 | | 选项 | VPC | 数据库 | 快速启动 | 模板链接 | 87 | | :--- | --- | ----- | :--------: | :-----: | 88 | | 选项一:从现有的 VPC 中部署基于 Aurora Serverless MySQL-Compatible 的 Keycloak | 现有 | Aurora Serverless MySQL-Compatible | [海外区域][Keycloak aurora serveless from existing VPC for Global]
[中国区域][Keycloak aurora serveless from existing VPC for China] | [下载][Keycloak aurora serverless from existing VPC template] | 89 | | 选项二:从新的 VPC 中部署基于 Aurora Serverless MySQL-Compatible 的 Keycloak | 新建 | Aurora Serverless MySQL-Compatible | [海外区域][Keycloak aurora serveless from new VPC for Global]
[中国区域][Keycloak aurora serveless from new VPC for China] | [下载][Keycloak aurora serverless from new VPC template] | 90 | | 选项三:从现有的 VPC 中部署基于 Aurora MySQL-Compatible 的 Keycloak | 现有 | Aurora MySQL-Compatible | [海外区域][Keycloak from existing VPC for Global]
[中国区域][Keycloak from existing VPC for China] | [下载][Keycloak from existing VPC template] | 91 | | 选项四:从新的 VPC 中部署基于 Aurora MySQL-Compatible 的 Keycloak | 新建 | Aurora MySQL-Compatible| [海外区域][Keycloak from new VPC for Global]
[中国区域][Keycloak from new VPC for China] | [下载][Keycloak from new VPC template] | 92 | 93 | ### 选项一:从现有的 VPC 中部署基于 Aurora Serverless MySQL-Compatible 的 Keycloak 94 | 95 | 1. 登录 [AWS CloudFormation][AWS CloudFormation console] 控制台。 96 | 97 | 2. 在左侧的导航窗中选择**堆栈**。 98 | 99 | 3. 点击**创建堆栈**并选择**使用新资源(标准)**。 100 | 101 | 4. 在**指定模板**部分执行以下操作: 102 | 1. 修改**准备模板**为**模板已就绪**。 103 | 2. 修改**模板源**为**上传模板文件**。 104 | 3. 点击**选择文件**并选择您下载的模板文件,例如 *keycloak-aurora-serverless-from-existing-vpc.template* 。 105 | 106 | 5. 选择**下一步**。 107 | 108 | 6. 在**指定堆栈详细信息**部分执行以下操作: 109 | 1. **堆栈名称**: 输入堆栈名称, 例如 *KeycloakOnAWS* 。 110 | 2. **CertificateArn**: 输入步骤 1. 创建 ACM 证书中记录的 **ARN**,例如 *arn:aws:acm:us-west-2:1436237113227:certificate/571518b3-123b-4502-1ec3-3t2sae704272*。 111 | 3. **Hostname**: 输入您的 Keycloak 的域名。若部署在中国区,域名需经过 ICP 备案。 112 | 4. **VpcId**: 选择现有的VPC 。 113 | 5. **PubSubnets**: 选择用于部署ALB的公有子网。 114 | 6. **PrivSubnets**: 选择用于部署ECS的私有子网。 115 | 7. **DBSubnets**: 选择用于部署数据库的私有子网。 116 | 8. **TaskCPU**: 为运行keycloak应用的Fargate Task指定CPU,默认为4096 (4 vCPU)。详见[Task CPU和内存][task cpu and memory]。 117 | 9. **TaskMemory**: 为运行keycloak应用的Fargate Task指定内存,默认为8192 MiB (8 GB)。请注意该值必须在您选择的TaskCPU允许的范围内,详见[Task CPU和内存][task cpu and memory]。 118 | 10. **MinContainers**: ECS容器的最小数量,默认值是2。 119 | 11. **MaxContainers**: ECS容器的最大数量,默认值是10。 120 | 12. **AutoScalingTargetCpuUtilization**: 弹性伸缩的CPU利用率百分比,默认值是75,最大值是100。 121 | 13. **JavaOpts**: JAVA_OPTS 参数。 122 | 123 | 7. 选择**下一步**。 124 | 125 | 8. 在**配置堆栈选项** 中可以添加 tags 。 126 | 127 | 9. 选择**下一步**。 128 | 129 | 10. 评审堆栈的配置信息,如您已确定配置信息,请勾选 **我确认,Amazon CloudFormation 可能创建 IAM 资源**。 130 | 131 | 11. 选择**创建堆栈**。 132 | 133 | ### 选项二:从新的 VPC 中部署基于 Aurora Serverless MySQL-Compatible 的 Keycloak 134 | 135 | 1. 登录 [AWS CloudFormation][AWS CloudFormation console] 控制台。 136 | 137 | 2. 在左侧的导航窗中选择**堆栈**。 138 | 139 | 3. 点击**创建堆栈**并选择**使用新资源(标准)**。 140 | 141 | 4. 在**指定模板**部分执行以下操作: 142 | 1. 修改**准备模板**为**模板已就绪**。 143 | 2. 修改**模板源**为**上传模板文件**。 144 | 3. 点击**选择文件**并选择您下载的模板文件,例如 *keycloak-aurora-serverless-from-new-vpc.template* 。 145 | 146 | 5. 选择**下一步**。 147 | 148 | 6. 在**指定堆栈详细信息**部分执行以下操作: 149 | 1. **堆栈名称**: 输入堆栈名称, 例如 *KeycloakOnAWS* 。 150 | 2. **CertificateArn**: 输入 步骤 1. 创建 ACM 证书 中记录的 **ARN**,例如 *arn:aws:acm:us-west-2:1436237113227:certificate/571518b3-123b-4502-1ec3-3t2sae704272*。 151 | 3. **Hostname**: 输入您的 Keycloak 的域名。若部署在中国区,域名需经过 ICP 备案。 152 | 4. **TaskCPU**: 为运行keycloak应用的Fargate Task指定CPU,默认为4096 (4 vCPU)。详见[Task CPU和内存][task cpu and memory]。 153 | 5. **TaskMemory**: 为运行keycloak应用的Fargate Task指定内存,默认为8192 MiB (8 GB)。请注意该值必须在您选择的TaskCPU允许的范围内,详见[Task CPU和内存][task cpu and memory]。 154 | 6. **MinContainers**: ECS容器的最小数量,默认值是2。 155 | 7. **MaxContainers**: ECS容器的最大数量,默认值是10。 156 | 8. **AutoScalingTargetCpuUtilization**: 弹性伸缩的CPU利用率百分比,默认值是75,最大值是100。 157 | 9. **JavaOpts**: JAVA_OPTS 参数。 158 | 159 | 7. 选择**下一步**。 160 | 161 | 8. 在**配置堆栈选项** 中可以添加 tags 。 162 | 163 | 9. 选择**下一步**。 164 | 165 | 10. 评审堆栈的配置信息,如您已确定配置信息,请勾选 **我确认,Amazon CloudFormation 可能创建 IAM 资源**。 166 | 167 | 11. 选择**创建堆栈**。 168 | 169 | ### 选项三:从现有的 VPC 中部署基于 Aurora MySQL-Compatible 的 Keycloak 170 | 171 | 1. 登录 [AWS CloudFormation][AWS CloudFormation console] 控制台。 172 | 173 | 2. 在左侧的导航窗中选择**堆栈**。 174 | 175 | 3. 点击**创建堆栈**并选择**使用新资源(标准)**。 176 | 177 | 4. 在**指定模板**部分执行以下操作: 178 | 1. 修改**准备模板**为**模板已就绪**。 179 | 2. 修改**模板源**为**上传模板文件**。 180 | 3. 点击**选择文件**并选择您下载的模板文件,例如 *keycloak-from-existing-vpc.template* 。 181 | 182 | 5. 选择**下一步**。 183 | 184 | 6. 在**指定堆栈详细信息**部分执行以下操作: 185 | 1. **堆栈名称**: 输入堆栈名称, 例如 *KeycloakOnAWS* 。 186 | 2. **CertificateArn**: 输入 步骤 1. 创建 ACM 证书 中记录的 **ARN**,例如 *arn:aws:acm:us-west-2:1436237113227:certificate/571518b3-123b-4502-1ec3-3t2sae704272*。 187 | 3. **Hostname**: 输入您的 Keycloak 的域名。若部署在中国区,域名需经过 ICP 备案。 188 | 4. **DatabaseInstanceType**: 选择数据库实例类型。 189 | 5. **VpcId**: 选择现有的VPC 。 190 | 6. **PubSubnets**: 选择用于部署ALB的公有子网。 191 | 7. **PrivSubnets**: 选择用于部署ECS的私有子网。 192 | 8. **DBSubnets**: 选择用于部署数据库的私有子网。 193 | 9. **TaskCPU**: 为运行keycloak应用的Fargate Task指定CPU,默认为4096 (4 vCPU)。详见[Task CPU和内存][task cpu and memory]。 194 | 10. **TaskMemory**: 为运行keycloak应用的Fargate Task指定内存,默认为8192 MiB (8 GB)。请注意该值必须在您选择的TaskCPU允许的范围内,详见[Task CPU和内存][task cpu and memory]。 195 | 11. **MinContainers**: ECS容器的最小数量,默认值是2。 196 | 12. **MaxContainers**: ECS容器的最大数量,默认值是10。 197 | 13. **AutoScalingTargetCpuUtilization**: 弹性伸缩的CPU利用率百分比,默认值是75,最大值是100。 198 | 14. **JavaOpts**: JAVA_OPTS 参数。 199 | 200 | 7. 选择**下一步**。 201 | 202 | 8. 在**第3步 配置堆栈选项** 中可以添加 tags 。 203 | 204 | 9. 选择**下一步**。 205 | 206 | 10. 评审堆栈的配置信息,如您已确定配置信息,请勾选 **我确认,Amazon CloudFormation 可能创建 IAM 资源**。 207 | 208 | 11. 选择**创建堆栈**。 209 | 210 | ### 选项四:从新的 VPC 中部署基于 Aurora MySQL-Compatible 的 Keycloak 211 | 212 | 1. 登录 [AWS CloudFormation][AWS CloudFormation console] 控制台。 213 | 214 | 2. 在左侧的导航窗中选择**堆栈**。 215 | 216 | 3. 点击**创建堆栈**并选择**使用新资源(标准)**。 217 | 218 | 4. 在**指定模板**部分执行以下操作: 219 | 1. 修改**准备模板**为**模板已就绪**。 220 | 2. 修改**模板源**为**上传模板文件**。 221 | 3. 点击**选择文件**并选择您下载的模板文件,例如 *keycloak-from-new-vpc.template* 。 222 | 223 | 5. 选择**下一步**。 224 | 225 | 6. 在**指定堆栈详细信息**部分执行以下操作: 226 | 1. **堆栈名称**: 输入堆栈名称, 例如 *KeycloakOnAWS* 。 227 | 2. **CertificateArn**: 输入 步骤 1. 创建 ACM 证书 中记录的 **ARN**,例如 *arn:aws:acm:us-west-2:1436237113227:certificate/571518b3-123b-4502-1ec3-3t2sae704272*。 228 | 3. **Hostname**: 输入您的 Keycloak 的域名。若部署在中国区,域名需经过 ICP 备案。 229 | 4. **DatabaseInstanceType**: 选择数据库实例类型。 230 | 5. **TaskCPU**: 为运行keycloak应用的Fargate Task指定CPU,默认为4096 (4 vCPU)。详见[Task CPU和内存][task cpu and memory]。 231 | 6. **TaskMemory**: 为运行keycloak应用的Fargate Task指定内存,默认为8192 MiB (8 GB)。请注意该值必须在您选择的TaskCPU允许的范围内,详见[Task CPU和内存][task cpu and memory]。 232 | 7. **MinContainers**: ECS容器的最小数量,默认值是2。 233 | 8. **MaxContainers**: ECS容器的最大数量,默认值是10。 234 | 9. **AutoScalingTargetCpuUtilization**: 弹性伸缩的CPU利用率百分比,默认值是75,最大值是100。 235 | 10. **JavaOpts**: JAVA_OPTS 参数。 236 | 237 | 7. 选择**下一步**。 238 | 239 | 8. 在**配置堆栈选项** 中可以添加 tags 。 240 | 241 | 9. 选择**下一步**。 242 | 243 | 10. 评审堆栈的配置信息,如您已确定配置信息,请勾选 **我确认,Amazon CloudFormation 可能创建 IAM 资源**。 244 | 245 | 11. 选择**创建堆栈**。 246 | 247 | ## 步骤 4. 在 Route 53 中创建记录以解析域名 248 | 249 | 1. 登录 [AWS CloudFormation][AWS CloudFormation console] 控制台。 250 | 251 | 2. 在左侧的导航窗中选择**堆栈**。 252 | 253 | 3. 选择新建的**堆栈名称**查看详细信息。 254 | 255 | 4. 选择**输出**。 256 | 257 | 5. 在搜索框中输入 **KeyCloakKeyCloakContainerSerivceEndpointURL** 并敲击回车,复制返回结果中的**值**,例如 *Keycl-KeyCl-1WIJGTSV19UTB-541714271.xx-xxx-1.elb.amazonaws.com*。 258 | 259 | 6. 登录 [Amazon Route 53][Amazon Route 53 console] 控制台。 260 | 261 | 7. 在左侧的导航窗中选择**托管区域**。 262 | 263 | 8. 点击**域名**查看详细信息。在新打开的页面中点击**创建记录**并执行以下操作: 264 | 1. 在**记录名称**中输入Keycloak的子域名,例如 *keycloak.yourdomain.com* 。 265 | 2. 修改**记录类型** 为 **CNAME** 。 266 | 3. 在**值**中输入第5步中复制的**KeyCloakKeyCloakContainerSerivceEndpointURL** 的 **值** ,例如 *Keycl-KeyCl-1WIJGTSV19UTB-541714271.xx-xxx-1.elb.amazonaws.com* 。 267 | 268 | 9. 选择**创建记录**。 269 | 270 | ## 步骤 5. 访问 Keycloak Web 控制台 271 | 272 | 1. 登录 [AWS Secrets Manager][AWS Secrets Manager console] 控制台 。 273 | 274 | 2. 在顶部的导航栏中选择您的Keycloak部署的**区域**。 275 | 276 | 3. 在左侧的导航窗中选择**密钥**。 277 | 278 | 4. 在过滤框中输入 **KeyCloakKCSecret** 并敲击回车。点击查询出的结果,例如 *KeyCloakKCSecretF8498E5C-VVSujKlsllRI* 。 279 | 280 | 5. 点击**密钥值**中的**检索密钥值**按钮。 281 | 282 | 6. 复制 **username** 和 **password** 。 283 | 284 | 7. 在浏览器中输入**您的 Keycloak 域名**,例如 *https://keycloak.yourdomain.com* 。 285 | 286 | 8. 点击 **Administration Console** 链接 。 287 | 288 | 9. 输入第6步中复制的 **username** 和 **password** ,点击 **Sign In** 。 289 | ![keycloak-login](../../images/implementation-guide/deployment/17-en-keycloak-login.png) 290 | 291 | 298 | 299 | [AWS Certificate Manager]: https://amazonaws.cn/certificate-manager/ 300 | [AWS Certificate Manager console]: https://console.aws.amazon.com/acm/home 301 | [AWS CloudFormation console]: https://console.aws.amazon.com/cloudformation/home 302 | [Amazon EC2 console]: https://console.aws.amazon.com/ec2 303 | [AWS Secrets Manager console]: https://console.aws.amazon.com/secretsmanager 304 | [Amazon Route 53 console]: https://console.aws.amazon.com/route53 305 | [Configuring Amazon Route 53 as your DNS service]: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-configuring.html 306 | [Keycloak aurora serveless from existing VPC for China]: https://console.amazonaws.cn/cloudformation/home?#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-aurora-serverless-from-existing-vpc.template 307 | [Keycloak aurora serveless from new VPC for China]: https://console.amazonaws.cn/cloudformation/home?#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.amazonaws.com/keycloakonaws/latest/keycloak-aurora-serverless-from-new-vpc.template 308 | [Keycloak from existing VPC for China]: https://console.amazonaws.cn/cloudformation/home?#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-from-existing-vpc.template 309 | [Keycloak from new VPC for China]: https://console.amazonaws.cn/cloudformation/home?#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-from-new-vpc.template 310 | [Keycloak aurora serveless from existing VPC for Global]: https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-aurora-serverless-from-existing-vpc.template 311 | [Keycloak aurora serveless from new VPC for Global]: https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.amazonaws.com/keycloakonaws/latest/keycloak-aurora-serverless-from-new-vpc.template 312 | [Keycloak from existing VPC for Global]: https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-from-existing-vpc.template 313 | [Keycloak from new VPC for Global]: https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateUrl=https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-from-new-vpc.template 314 | [Keycloak aurora serverless from existing VPC template]: https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-aurora-serverless-from-existing-vpc.template 315 | [Keycloak aurora serverless from new VPC template]: https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-aurora-serverless-from-new-vpc.template 316 | [Keycloak from existing VPC template]: https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-from-existing-vpc.template 317 | [Keycloak from new VPC template]: https://aws-gcr-solutions.s3.cn-north-1.amazonaws.com.cn/keycloakonaws/latest/keycloak-from-new-vpc.template 318 | [comparisons]: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html#Serverless.v1-v2-requirements 319 | [task cpu and memory]: https://docs.aws.amazon.com/AmazonECS/latest/userguide/fargate-task-defs.html#fargate-tasks-size 320 | 321 | 322 | 323 | -------------------------------------------------------------------------------- /docs/zh/implementation-guide/revisions.md: -------------------------------------------------------------------------------- 1 | # 版本更新 2 | 3 | | Date | Description | 4 | | ------------ | :------------------------------ | 5 | | 2022年7月 | 发布版本2.1.0
1. 升级 Keycloak 版本至 16.1.1
2. 升级 aws-cdk 版本至 1.160.0
3. 升级 cdk-keycloak 版本至 0.2.41 | 6 | | 2023年10月| 发布版本2.1.6
1. 升级 Keycloak 版本至 22.0.4
2. 升级 aws-cdk 版本至 2.100.0
3. 升级 cdk-keycloak 版本至 2.9.0 | 7 | 8 | -------------------------------------------------------------------------------- /docs/zh/implementation-guide/tutorials/ad-ldap.md: -------------------------------------------------------------------------------- 1 | # 教程: 如何将 Keycloak 与 AD/LDAP 集成? 2 | 3 | Keycloak 允许用户与 AD/LDAP 联合。本教程将引导您完成使用 OpenLDAP 服务的用户联合。了解更多信息, 请参考 [User Federation](https://www.keycloak.org/docs/latest/server_admin/#_user-storage-federation)。 4 | 5 | Keycloak 支持多种 LDAP 服务,包括 Microsoft AD 和 OpenLDAP。本教程将在同一个 VPC 中运行一个 OpenLDAP 服务,并在此演示中使用 Keycloak 服务。 6 | 7 | ## 前提条件 8 | 9 | 您已经通过 AWS CloudFormation 或 AWS CDK 部署了解决方案,并且已经以 Keycloak 管理员用户身份成功登录了 Keycloak 仪表板。 10 | 11 | ## 操作步骤 12 | 13 | 步骤 1. 启动一个用于安装 OpenLDAP 的 EC2 实例 14 | 15 | 步骤 2. 安装 OpenLDAP 16 | 17 | 步骤 3. 在 Keycloak 上创建 User Federation 18 | 19 | 步骤 4. 验证 User Federation 20 | 21 | ## 步骤 1. 启动一个用于安装 OpenLDAP 的 EC2 实例 22 | 23 | 您需要在您的 Keycloak 服务所在的 VPC 中启动 EC2 实例,然后执行以下操作配置此 EC2 实例的安全组,并确保来自 VPC CIDR 的所有流量都可以访问其 LDAP 端口(TCP 389)。 24 | 25 | 1. 打开 [Amazon EC2 ][Amazon EC2 console] 控制台。 26 | 27 | 2. 在左侧的导航窗中选择 **Security Groups**。 28 | 29 | 3. 在过滤框中输入 `KeyCloakKeyCloakContainer` 后按回车键,复制 **Security group ID**,例如 *sg-0121f1140bbfd72c6*。 30 | 31 | 4. 进入 EC2 实例所在的安全组,添加 Inbound rules,允许 ECS 所在的安全组访问 OpenLDAP 的端口。 32 | 33 | 5. 点击 **Save rules** 。 34 | 35 | ## 步骤 2. 安装 OpenLDAP 36 | 37 | 在您的 EC2 实例中使用 Docker 安装 OpenLDAP。 38 | 39 | 1. 连接终端到您的 EC2 实例上。 40 | 2. 执行以下操作: 41 | ``` 42 | # 安装 docker,并启动 docker服务 43 | yum install -y docker 44 | systemctl start docker 45 | # 启动 docker 容器 46 | docker run -p 389:1389 public.ecr.aws/bitnami/openldap:latest 47 | ``` 48 | 49 | 3. 打开另一个终端并安装OpenLDAP客户端 50 | ``` 51 | # 安装LDAP客户端 52 | yum install -y openldap-clients 53 | # 查看所有用户信息 54 | ldapsearch -x -b "ou=users,dc=example,dc=org" -H ldap:// 55 | ``` 56 | 示例: 57 | ``` 58 | [root@xxxx ~]# ldapsearch -x -b "ou=users,dc=example,dc=org" -H ldap:// 59 | # extended LDIF 60 | # 61 | # LDAPv3 62 | # base with scope subtree 63 | # filter: (objectclass=*) 64 | # requesting: ALL 65 | # 66 | 67 | # users, example.org 68 | dn: ou=users,dc=example,dc=org 69 | objectClass: organizationalUnit 70 | ou: users 71 | 72 | # user01, users, example.org 73 | dn: cn=user01,ou=users,dc=example,dc=org 74 | cn: User1 75 | cn: user01 76 | sn: Bar1 77 | objectClass: inetOrgPerson 78 | objectClass: posixAccount 79 | objectClass: shadowAccount 80 | userPassword:: Yml0bmFtaTE= 81 | uid: user01 82 | uidNumber: 1000 83 | gidNumber: 1000 84 | homeDirectory: /home/user01 85 | 86 | # user02, users, example.org 87 | dn: cn=user02,ou=users,dc=example,dc=org 88 | cn: User2 89 | cn: user02 90 | sn: Bar2 91 | objectClass: inetOrgPerson 92 | objectClass: posixAccount 93 | objectClass: shadowAccount 94 | userPassword:: Yml0bmFtaTI= 95 | uid: user02 96 | uidNumber: 1001 97 | gidNumber: 1001 98 | homeDirectory: /home/user02 99 | 100 | # readers, users, example.org 101 | dn: cn=readers,ou=users,dc=example,dc=org 102 | cn: readers 103 | objectClass: groupOfNames 104 | member: cn=user01,ou=users,dc=example,dc=org 105 | member: cn=user02,ou=users,dc=example,dc=org 106 | 107 | # search result 108 | search: 2 109 | result: 0 Success 110 | 111 | # numResponses: 5 112 | # numEntries: 4 113 | ``` 114 | 115 | 现在您的默认 LDAP 服务已准备就绪。 116 | 117 | ## 步骤 3. 在 Keycloak 上创建 User Federation 118 | 119 | 1. 以 **Keycloak** 管理员用户身份登录到 Keycloak 仪表板。 120 | 121 | 2. 在左侧的导航窗中,选择 **User Federation**。 122 | 123 | 3. 在 **Add provider** 下拉菜单中选择 **ldap**。 124 | 125 | 4. 在新打开的页面中输入以下信息: 126 | 1. **Edit Mode**: 修改为 `WRITABLE`。 127 | 2. **Vendor**: 修改为 `Other`。 128 | 3. **Username LDAP attribute**: 输入您的 LDAP attribute name for username,在本教程中使用 `cn`。 129 | 4. **RDN LDAP attribute**: 输入您的 LDAP attribute name for user RDN,在本教程中使用 `cn`。 130 | 5. **UUID LDAP attribute**: 输入您的 LDAP attribute name for UUID,在本教程中使用 `uid`。 131 | 6. **User Object Classes**: 输入您的 LDAP User Object Classes,在本教程中使用 `inetOrgPerson, posixAccount, shadowAccount`。 132 | 7. **Connection URL**: 输入您的 LDAP connection URL,在本教程中使用 `ldap://`,点击 **Test connection** 后提示`"Success! LDAP connection successful."`信息说明LDAP连接正常。 133 | 8. **Users DN**: 输入您的 LDAP Users DN,在本教程中使用 `ou=users,dc=example,dc=org` 。 134 | 9. **Bind Type**: 修改为 `simple`。 135 | 10. **Bind DN**: 输入您的 LDAP bind DN,在本教程中使用 `cn=admin,dc=example,dc=org` 。 136 | 11. **Bind Credential**: 输入您的 LDAP Bind Credentials,在本教程中使用 `adminpassword`,点击 **Test authentication**后提示`"Success! LDAP authentication successful."`信息说明LDAP认证成功。 137 | 138 | 5. 选择 **Save**。 139 | 140 | 6. 选择 **Synchronize all users**。页面提示"Success! Sync of users finished successfully. 2 imported users, 0 updated users"。 141 | 142 | 7. 在左侧的导航窗中选择 **Users** 。 143 | 144 | 8. 点击 **View all users**,查看到 **user1** 和 **user2** 用户说明导入成功。 145 | 146 | ## 步骤 4. 验证 User Federation 147 | 148 | 使用 **account-console** 登录验证 User Federation。 149 | 150 | 1. 以 **Keycloak** 管理员用户身份登录到 Keycloak 仪表板。 151 | 152 | 2. 在左侧的导航窗中,选择 **Clients** 。 153 | 154 | 3. 选择 **account-console** 的 **Base URL** 。 155 | 156 | 4. 您将被重定向到 Keycloak 帐户控制台,单击右上角的 **Sign In** 按钮。 157 | ![06-en-keycloak-account-console-signin-01](../../images/implementation-guide/tutorial/ad-ldap/06-en-keycloak-account-console-signin-01.png) 158 | 159 | 5. 在 Username or email 中输入 **user1** to Username or email,在 Password中输入 **bitnami1**。 160 | 161 | 6. 选择 **Sign In**。您已经成功登录到控制台。 162 | 163 | ## 常见问题解答 164 | 165 | **1. Keycloak 是否支持 LDAPS 协议?** 166 | 167 | 是的。Keycloak 同时支持 ldap:// 和 ldaps://。要启用 ldaps://,请确保您的 AD/LDAP 使用 LDAPS 运行并且已正确导入证书。 168 | 169 | **2. 如果我正在运行 Microsoft AD 服务器,我应该选择哪种供应商类型?** 170 | 171 | 在Vendor参数中选择 Active Directory。 172 | ![07-en-keycloak-user-federation-provider](../../images/implementation-guide/tutorial/ad-ldap/07-en-keycloak-user-federation-provider.png) 173 | 174 | 175 | [Amazon Certificate Manager]: https://aws.amazon.com/cn/certificate-manager/ 176 | [AWS Certificate Manager console]: https://console.aws.amazon.com/acm/home 177 | [AWS CloudFormation console]: https://console.aws.amazon.com/cloudformation/home 178 | [Amazon EC2 console]: https://console.aws.amazon.com/ec2 179 | [AWS Secrets Manager console]: https://console.aws.amazon.com/secretsmanager 180 | [Amazon Route 53 console]: https://console.aws.amazon.com/route53 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/zh/implementation-guide/tutorials/api-gateway.md: -------------------------------------------------------------------------------- 1 | # 教程: 如何将 Keycloak 与 Amazon API Gateway 集成? 2 | 3 | 本教程介绍如何通过 Keycloak 控制不同用户访问不同API接口的权限。更多信息可以参考 Keycloak 文档的 [Authorization Services](https://www.keycloak.org/docs/16.1/authorization_services/index.html) 章节。 4 | 5 | ## 架构图 6 | ![01-en-architecture-diagram](../../images/implementation-guide/tutorial/api-gateway/01-en-architecture-diagram.svg) 7 | 8 | ## 前提条件 9 | 10 | 1. 您已经通过 AWS CloudFormation 或 AWS CDK 部署了解决方案,并且已经以 Keycloak 管理员用户身份成功登录 Keycloak 仪表板。 11 | 12 | 2. 确保在 CloudFormation 参数中填写了以下 JAVA_OPTS。 13 | ``` 14 | -Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.profile.feature.upload_scripts=enabled 15 | ``` 16 | 17 | ## 操作步骤 18 | 19 | 步骤 1. 克隆keycloak-on-aws代码至本地 20 | 21 | 步骤 2. 导入Keycloak示例配置文件 22 | 23 | 步骤 3. 在本地环境运行serverless-express-auth 24 | 25 | 步骤 4. 在本地环境运行Vue UI 26 | 27 | 步骤 5. 验证用户权限 28 | 29 | ## 步骤 1. 克隆keycloak-on-aws代码至本地 30 | 31 | 执行以下命令将 keycloak-on-aws 代码下载到本地。 32 | 33 | ``` 34 | git clone https://github.com/aws-samples/keycloak-on-aws.git 35 | cd keycloak-on-aws 36 | ``` 37 | 38 | ## 步骤 2. 导入Keycloak示例配置文件 39 | 40 | 1. 以 **Keycloak** 管理员用户身份登录 Keycloak 仪表板。 41 | 42 | 2. 在左侧导航窗中的 **Master**,选择 **Add realm**。 43 | 44 | 3. 选择 **Select file**,选中步骤 1 下载代码中的 **tutorials/api-gateway/resoures/keycloak.json** 文件 。 45 | ![02-en-keycloak-add-realm](../../images/implementation-guide/tutorial/api-gateway/02-en-keycloak-add-realm.png) 46 | 47 | 5. 选择 **Create** 。 48 | 49 | ## 步骤 3. 在本地环境运行serverless-express-auth 50 | 51 | 该示例主要基于 [expressjs](https://github.com/expressjs/express) 和官方的 [keycloak-nodejs-connect](https://github.com/keycloak/keycloak-nodejs-connect),并使用 [serverless-express](https://github.com/vendia/serverless-express/) 从而让 expressjs 能够在 Lambda 上运行。 52 | 53 | keycloak-nodejs-connect 由 Keycloak 团队维护,这是连接到 Keycloak 的推荐方式。 54 | 55 | !!! Note "说明" 56 | keycloak-nodejs-connect的文件位于 https://www.keycloak.org/docs/latest/securing_apps/#_nodejs_adapter。 57 | 58 | 1. 以 **Keycloak** 管理员用户身份登录 Keycloak 仪表板。 59 | 60 | 2. 在左侧的导航窗中选择 **Clients**。 61 | 62 | 3. 选择 **vue** 查看详细信息。 63 | 64 | 4. 选择 **Installation**。 65 | 66 | 5. 点击 **Format Option** 下拉菜单并选择 **Keycloak OIDC JSON**。 67 | 68 | 6. 根据 Keyloak Installation 中的配置更新步骤 1 下载代码中 **tutorials/api-gateway/resources/keycloak.json** 文件内容 。 69 | ``` 70 | { 71 | "realm": "keycloak-on-aws", 72 | "auth-server-url": "https://keycloak.yourdomain.com/auth/", 73 | "ssl-required": "external", 74 | "resource": "vue", 75 | "public-client": true, 76 | "confidential-port": 0, 77 | // Don't remove the following line, this is for vue-ui to contact to api gw !!! 78 | "x-api-gw-url": "http://localhost:3003/dev/hello" 79 | } 80 | ``` 81 | 7. 进入 **tutorials/api-gateway/serverless-express-auth** 目录,并执行以下操作: 82 | ``` 83 | cd tutorials/api-gateway/serverless-express-auth 84 | yarn 85 | yarn offline 86 | ``` 87 | 输出示例 88 | ``` 89 | yarn run v1.22.19 90 | $ SLS_DEBUG=* serverless offline --host 0.0.0.0 --httpPort 3003 --lambdaPort=3019 91 | ... 92 | offline: Offline [http for lambda] listening on http://0.0.0.0:3019 93 | offline: Function names exposed for local invocation by aws-sdk: 94 | * authEndpoint: serverless-express-auth-dev-authEndpoint 95 | * hello: serverless-express-auth-dev-hello 96 | [offline] Lambda Invocation Routes (for AWS SDK or AWS CLI): 97 | * POST http://0.0.0.0:3019/2015-03-31/functions/serverless-express-auth-dev-authEndpoint/invocations 98 | * POST http://0.0.0.0:3019/2015-03-31/functions/serverless-express-auth-dev-hello/invocations 99 | [offline] Lambda Async Invocation Routes (for AWS SDK or AWS CLI): 100 | * POST http://0.0.0.0:3019/2014-11-13/functions/serverless-express-auth-dev-authEndpoint/invoke-async/ 101 | * POST http://0.0.0.0:3019/2014-11-13/functions/serverless-express-auth-dev-hello/invoke-async/ 102 | offline: Configuring Authorization: hello authEndpoint 103 | [offline] Creating Authorization scheme for hello-authEndpoint-GET-hello 104 | 105 | ┌───────────────────────────────────────────────────────────────────────┐ 106 | │ │ 107 | │ GET | http://0.0.0.0:3003/dev/hello │ 108 | │ POST | http://0.0.0.0:3003/2015-03-31/functions/hello/invocations │ 109 | │ │ 110 | └───────────────────────────────────────────────────────────────────────┘ 111 | 112 | offline: [HTTP] server ready: http://0.0.0.0:3003 🚀 113 | offline: 114 | offline: Enter "rp" to replay the last request 115 | ``` 116 | 117 | 157 | 158 | ## 步骤 4. 在本地环境运行Vue UI 159 | 160 | ``` 161 | cd tutorials/api-gateway/vue-ui 162 | yarn 163 | yarn serve 164 | ``` 165 | 输出示例 166 | ``` 167 | yarn run v1.22.19 168 | $ vue-cli-service serve 169 | Browserslist: caniuse-lite is outdated. Please run: 170 | npx browserslist@latest --update-db 171 | Why you should do it regularly: https://github.com/browserslist/browserslist#browsers-data-updating 172 | INFO Starting development server... 173 | Starting type checking service... 174 | Using 1 worker with 2048MB memory limit 175 | 98% after emitting CopyPlugin 176 | 177 | DONE Compiled successfully in 1638ms 3:42:04 PM 178 | 179 | No type errors found 180 | Version: typescript 4.2.4 181 | Time: 1111ms 182 | 183 | App running at: 184 | - Local: http://localhost:8080/ 185 | - Network: http://localhost:8080/ 186 | 187 | Note that the development build is not optimized. 188 | To create a production build, run yarn build. 189 | ``` 190 | 191 | ## 步骤 5. 验证用户权限 192 | 193 | 一个常见的场景是不同的用户有不同的权限来执行一个动作(允许/拒绝)。此教程中内置了两个不同的用户,user1 可以调用 API gateway,而 user2 不允许。 194 | 195 | 用户详细信息如下: 196 | 197 | |用户名|密码|角色|描述| 198 | |---|---|---|:---| 199 | |user1|user1|call-api|user1 允许调用 API gateway| 200 | |user2|user2|-|user2 不允许调用 API gateway| 201 | 202 | 1. 打开**Vue UI** 控制台,例如 *http://localhost:8080* 。 203 | 204 | 2. 点击 **Login**。 205 | 206 | 3. 在 Username or email中 输入 **user1**,在 Password 中输入 **user1**。 207 | 208 | 4. 选择 **Sign In**。 209 | 210 | 5. 选择 **Request**,您将获得成功的响应消息。 211 | ``` 212 | { 213 | "url": "http://localhost:3003/dev/hello", 214 | "status": 200, 215 | "statusText": "OK", 216 | "data": { 217 | "message": "Hello World from protect server" 218 | } 219 | } 220 | ``` 221 | 222 | 6. 点击 **Logout**. 223 | 224 | 7. 在 Username or email中 输入 **user2**,在 Password 中输入 **user2**。 225 | 226 | 8. 选择 **Sign In**。 227 | 228 | 9. 选择 **Request**,您将获得失败的响应消息,状态码为 401。 229 | ``` 230 | { 231 | "url": "http://localhost:3003/dev/hello", 232 | "status": 401, 233 | "statusText": "Unauthorized", 234 | "data": { 235 | "statusCode": 401, 236 | "error": "Unauthorized", 237 | "message": "Unauthorized" 238 | } 239 | } 240 | ``` 241 | 242 | ## 常见问题解答 243 | 244 | **1. 如何导出 Keycloak 域用户?** 245 | 246 | 运行以下命令导出 Keycloak 域用户: 247 | 248 | ``` 249 | $ docker exec 250 | $ /opt/jboss/keycloak/bin/standalone.sh -Dkeycloak.migration.action=export -Dkeycloak.migration.realmName=keycloak-on-aws -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.file=realm-export.json -Djboss.socket.binding.port-offset=99 251 | ``` 252 | 253 | 详情请参考: 254 | 255 | [Amazon Certificate Manager]: https://aws.amazon.com/cn/certificate-manager/ 256 | [AWS Certificate Manager console]: https://console.aws.amazon.com/acm/home 257 | [AWS CloudFormation console]: https://console.aws.amazon.com/cloudformation/home 258 | [Amazon EC2 console]: https://console.aws.amazon.com/ec2 259 | [AWS Secrets Manager console]: https://console.aws.amazon.com/secretsmanager 260 | [Amazon Route 53 console]: https://console.aws.amazon.com/route53 261 | 262 | 263 | -------------------------------------------------------------------------------- /docs/zh/index.md: -------------------------------------------------------------------------------- 1 | 通过此解决方案您可以快速在 AWS 云上构建高可用架构的 Keycloak 集群,用以实现标准化的身份和访问控制系统。Keycloak 是一款开源的身份和访问控制软件,提供单点登录功能,以及可自定义的用户界面,用于登录、注册和帐户管理等。您可将其集成到现有的 LDAP 和 Azure Active Directory 服务器中,也可以将身份验证委派给第三方身份提供商。 2 | 3 | 该解决方案主要包括以下特点: 4 | 5 | - **单点登录**: 支持 OpenID Connect、OAuth 2.0 和 SAML 2.0 标准协议。 6 | 7 | - **身份和访问管理**: 提供用户联合、强认证、用户管理、细粒度授权等。轻松的添加身份验证到应用程序和服务中,而无需处理存储用户或验证用户。 8 | 9 | ## 术语 10 | 11 | 以下是相关术语的说明。 12 | 13 | | 术语 | 全称 | 描述 | 14 | | --- | --- | :--- | 15 | | SSO | Single-Sign On | Single-Sign On 是一种身份验证方案,它允许用户使用单个 ID 登录到任何一个关但独立的软件系统中。 | 16 | OAuth | Open Authorization | Open Authorization 是访问授权的开放标准,通常用作互联网用户授予网站或应用程序访问其在其它网站上的信息但不给密码的一种方式。 | 17 | OpenID | OpenID | OpenID 是由非营利性 OpenID 基金会推动的开放标准和去中心化的身份验证协议。 有关详细信息,请参阅[OpenID Connect](https://en.wikipedia.org/wiki/OpenID#OpenID_Connect_(OIDC))。 | 18 | ICP | Internet Content Provider | ICP 许可证是中国工业和信息化部(MIIT)颁发的允许中国网站在中国运营的许可证。 | 19 | JWT | JSON Web Token | JSON Web Token 是一种提议的 Internet 标准,用于创建具有可选签名和/或可选加密的数据,其有效负载包含断言一定数量的声明的 JSON。令牌使用私有密钥或公钥/私钥进行签名。| 20 | LDAP | Lightweight Directory Access Protocol | Lightweight Directory Access Protocol 是一种开放的、供应商中立的、行业标准的应用协议,用于通过 Internet 协议 (IP) 网络访问和维护分布式目录信息服务。 | 21 | AD | Active Directory | Active Directory 是 Microsoft 为 Windows 域网络开发的目录服务。 它作为一组进程和服务包含在大多数 Windows Server 操作系统中。| 22 | 23 | 本实施指南描述了在 AWS 云中部署 Keycloak 解决方案的架构注意事项和配置步骤。它包括指向 [CloudFormation][cloudformation] 模板的链接,这些模板使用 AWS 安全性和可用性最佳实践来启动和配置部署此解决方案所需的 AWS 服务。 24 | 25 | 该指南面向具有 AWS 云架构实践经验的 IT 架构师、开发人员和 DevOps。 26 | 27 | [cloudformation]: https://aws.amazon.com/en/cloudformation/ -------------------------------------------------------------------------------- /source/.env: -------------------------------------------------------------------------------- 1 | if [ -z "${DIST_OUTPUT_BUCKET}" ]; then 2 | export DIST_OUTPUT_BUCKET="the-bucket" 3 | fi 4 | if [ -z "${SOLUTION_NAME}" ]; then 5 | export SOLUTION_NAME="keycloak-on-aws" 6 | fi 7 | -------------------------------------------------------------------------------- /source/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true, 4 | "node": true 5 | }, 6 | "root": true, 7 | "plugins": [ 8 | "@typescript-eslint", 9 | "import" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": 2018, 14 | "sourceType": "module", 15 | "project": "./tsconfig.jest.json" 16 | }, 17 | "extends": [ 18 | "plugin:import/typescript" 19 | ], 20 | "settings": { 21 | "import/parsers": { 22 | "@typescript-eslint/parser": [ 23 | ".ts", 24 | ".tsx" 25 | ] 26 | }, 27 | "import/resolver": { 28 | "node": {}, 29 | "typescript": { 30 | "directory": "./tsconfig.json" 31 | } 32 | } 33 | }, 34 | "ignorePatterns": [ 35 | "*.js", 36 | "!.projenrc.js", 37 | "*.d.ts", 38 | "node_modules/", 39 | "*.generated.ts", 40 | "coverage" 41 | ], 42 | "rules": { 43 | "@typescript-eslint/no-require-imports": [ 44 | "error" 45 | ], 46 | "indent": [ 47 | "off" 48 | ], 49 | "@typescript-eslint/indent": [ 50 | "error", 51 | 2 52 | ], 53 | "quotes": [ 54 | "error", 55 | "single", 56 | { 57 | "avoidEscape": true 58 | } 59 | ], 60 | "comma-dangle": [ 61 | "error", 62 | "always-multiline" 63 | ], 64 | "comma-spacing": [ 65 | "error", 66 | { 67 | "before": false, 68 | "after": true 69 | } 70 | ], 71 | "no-multi-spaces": [ 72 | "error", 73 | { 74 | "ignoreEOLComments": false 75 | } 76 | ], 77 | "array-bracket-spacing": [ 78 | "error", 79 | "never" 80 | ], 81 | "array-bracket-newline": [ 82 | "error", 83 | "consistent" 84 | ], 85 | "object-curly-spacing": [ 86 | "error", 87 | "always" 88 | ], 89 | "object-curly-newline": [ 90 | "error", 91 | { 92 | "multiline": true, 93 | "consistent": true 94 | } 95 | ], 96 | "object-property-newline": [ 97 | "error", 98 | { 99 | "allowAllPropertiesOnSameLine": true 100 | } 101 | ], 102 | "keyword-spacing": [ 103 | "error" 104 | ], 105 | "brace-style": [ 106 | "error", 107 | "1tbs", 108 | { 109 | "allowSingleLine": true 110 | } 111 | ], 112 | "space-before-blocks": [ 113 | "error" 114 | ], 115 | "curly": [ 116 | "error", 117 | "multi-line", 118 | "consistent" 119 | ], 120 | "@typescript-eslint/member-delimiter-style": [ 121 | "error" 122 | ], 123 | "import/no-extraneous-dependencies": [ 124 | "error", 125 | { 126 | "devDependencies": [ 127 | "**/build-tools/**", 128 | "**/test/**" 129 | ], 130 | "optionalDependencies": false, 131 | "peerDependencies": true 132 | } 133 | ], 134 | "import/no-unresolved": [ 135 | "error" 136 | ], 137 | "import/order": [ 138 | "warn", 139 | { 140 | "groups": [ 141 | "builtin", 142 | "external" 143 | ], 144 | "alphabetize": { 145 | "order": "asc", 146 | "caseInsensitive": true 147 | } 148 | } 149 | ], 150 | "no-duplicate-imports": [ 151 | "error" 152 | ], 153 | "no-shadow": [ 154 | "off" 155 | ], 156 | "@typescript-eslint/no-shadow": [ 157 | "error" 158 | ], 159 | "key-spacing": [ 160 | "error" 161 | ], 162 | "semi": [ 163 | "error", 164 | "always" 165 | ], 166 | "quote-props": [ 167 | "error", 168 | "consistent-as-needed" 169 | ], 170 | "no-multiple-empty-lines": [ 171 | "error" 172 | ], 173 | "max-len": [ 174 | "error", 175 | { 176 | "code": 150, 177 | "ignoreUrls": true, 178 | "ignoreStrings": true, 179 | "ignoreTemplateLiterals": true, 180 | "ignoreComments": true, 181 | "ignoreRegExpLiterals": true 182 | } 183 | ], 184 | "@typescript-eslint/no-floating-promises": [ 185 | "error" 186 | ], 187 | "no-return-await": [ 188 | "off" 189 | ], 190 | "@typescript-eslint/return-await": [ 191 | "error" 192 | ], 193 | "no-trailing-spaces": [ 194 | "error" 195 | ], 196 | "dot-notation": [ 197 | "error" 198 | ], 199 | "no-bitwise": [ 200 | "error" 201 | ], 202 | "@typescript-eslint/member-ordering": [ 203 | "error", 204 | { 205 | "default": [ 206 | "public-static-field", 207 | "public-static-method", 208 | "protected-static-field", 209 | "protected-static-method", 210 | "private-static-field", 211 | "private-static-method", 212 | "field", 213 | "constructor", 214 | "method" 215 | ] 216 | } 217 | ] 218 | }, 219 | "overrides": [ 220 | { 221 | "files": [ 222 | ".projenrc.js" 223 | ], 224 | "rules": { 225 | "@typescript-eslint/no-require-imports": "off", 226 | "import/no-extraneous-dependencies": "off" 227 | } 228 | } 229 | ] 230 | } -------------------------------------------------------------------------------- /source/.npmignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | /.projenrc.js 3 | /.projen 4 | /.versionrc.json 5 | # jest-junit artifacts 6 | /test-reports/ 7 | junit.xml 8 | /coverage 9 | /test 10 | /.mergify.yml 11 | /tsconfig.json 12 | !/lib 13 | /src 14 | !/lib/**/*.js 15 | !/lib/**/*.d.ts 16 | dist 17 | /.github 18 | /.vscode 19 | /.idea 20 | /tsconfig.jest.json 21 | /.eslintrc.json 22 | cdk.out/ 23 | .cdk.staging/ -------------------------------------------------------------------------------- /source/.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageFiles": [ 3 | { 4 | "filename": "version.json", 5 | "type": "json" 6 | } 7 | ], 8 | "bumpFiles": [ 9 | { 10 | "filename": "version.json", 11 | "type": "json" 12 | }, 13 | { 14 | "filename": "package.json", 15 | "type": "json" 16 | } 17 | ], 18 | "commitAll": true 19 | } 20 | -------------------------------------------------------------------------------- /source/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts src/main.ts" 3 | } -------------------------------------------------------------------------------- /source/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keycloak-on-aws", 3 | "scripts": { 4 | "test": "rm -fr lib && npm run test:compile && jest --passWithNoTests --all --updateSnapshot && eslint", 5 | "build": "npm run test && npm run synth", 6 | "bump": "standard-version -i ../CHANGELOG.md", 7 | "test:watch": "jest --watch", 8 | "test:update": "jest --updateSnapshot", 9 | "test:compile": "tsc --noEmit --project tsconfig.jest.json", 10 | "watch": "tsc -w", 11 | "eslint": "eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern src test", 12 | "cdk": "cdk", 13 | "diff": "cdk diff", 14 | "synth": "cdk synth", 15 | "deploy": "cdk deploy", 16 | "destroy": "cdk destroy" 17 | }, 18 | "devDependencies": { 19 | "@types/jest": "^27", 20 | "@types/node": "^16", 21 | "@typescript-eslint/eslint-plugin": "^6", 22 | "@typescript-eslint/parser": "^6", 23 | "aws-cdk":"^2.100.0", 24 | "eslint": "^8", 25 | "eslint-import-resolver-node": "^0.3.9", 26 | "eslint-import-resolver-typescript": "^2.7.1", 27 | "eslint-plugin-import": "^2.28.1", 28 | "jest": "^27", 29 | "jest-junit": "^15", 30 | "json-schema": "^0.4.0", 31 | "path-parse": "1.0.7", 32 | "standard-version": "^9", 33 | "trim-newlines": "4.0.2", 34 | "ts-jest": "^27", 35 | "ts-node": "10.8.1", 36 | "typescript": "4.9.5" 37 | }, 38 | "dependencies": { 39 | "aws-cdk-lib": "^2.100.0", 40 | "constructs": "^10.0.5", 41 | "cdk-bootstrapless-synthesizer": "^2.3.2", 42 | "cdk-keycloak": "2.9.0" 43 | }, 44 | "bundledDependencies": [], 45 | "license": "Apache-2.0", 46 | "version": "2.1.0", 47 | "jest": { 48 | "clearMocks": true, 49 | "collectCoverage": true, 50 | "coverageDirectory": "coverage", 51 | "coveragePathIgnorePatterns": [ 52 | "/node_modules/" 53 | ], 54 | "testPathIgnorePatterns": [ 55 | "/node_modules/" 56 | ], 57 | "testMatch": [ 58 | "**/__tests__/**/*.ts?(x)", 59 | "**/?(*.)+(spec|test).ts?(x)" 60 | ], 61 | "reporters": [ 62 | "default", 63 | [ 64 | "jest-junit", 65 | { 66 | "outputDirectory": "test-reports" 67 | } 68 | ] 69 | ], 70 | "preset": "ts-jest", 71 | "globals": { 72 | "ts-jest": { 73 | "tsconfig": "tsconfig.jest.json" 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /source/src/main.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'aws-cdk-lib'; 2 | import { BootstraplessStackSynthesizer } from 'cdk-bootstrapless-synthesizer'; 3 | import { KeycloakStack } from './stack'; 4 | 5 | const app = new App(); 6 | 7 | mkstack(app, 'keycloak-aurora-serverless-from-existing-vpc'); 8 | mkstack(app, 'keycloak-aurora-serverless-from-new-vpc'); 9 | mkstack(app, 'keycloak-from-existing-vpc'); 10 | mkstack(app, 'keycloak-from-new-vpc'); 11 | 12 | app.synth(); 13 | 14 | function mkstack(a: App, id: string) { 15 | return new KeycloakStack(a, id, { 16 | auroraServerlessV2: id.includes('aurora'), 17 | fromExistingVPC: id.includes('existing-vpc'), 18 | synthesizer: newSynthesizer(), 19 | }); 20 | } 21 | 22 | function newSynthesizer() { 23 | return process.env.USE_BSS ? new BootstraplessStackSynthesizer(): undefined; 24 | } -------------------------------------------------------------------------------- /source/src/stack.ts: -------------------------------------------------------------------------------- 1 | import * as ec2 from 'aws-cdk-lib/aws-ec2'; 2 | import { Stack, StackProps, CfnParameter, CfnParameterProps, Fn, Aws, Duration,} from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import { KeyCloak, KeycloakVersion } from 'cdk-keycloak'; 5 | 6 | export class SolutionStack extends Stack { 7 | private _paramGroup: { [grpname: string]: CfnParameter[]} = {} 8 | 9 | protected setDescription(description: string) { this.templateOptions.description = description; } 10 | protected makeParam(id: string, props?: CfnParameterProps): CfnParameter { return new CfnParameter(this, id, props); } 11 | protected addGroupParam(props: { [key: string]: CfnParameter[]}): void { 12 | for (const key of Object.keys(props)) { 13 | const params = props[key]; 14 | this._paramGroup[key] = params.concat(this._paramGroup[key] ?? []); 15 | } 16 | this._setParamGroups(); 17 | } 18 | private _setParamGroups(): void { 19 | if (!this.templateOptions.metadata) { this.templateOptions.metadata = {}; } 20 | const mkgrp = (label: string, params: CfnParameter[]) => { 21 | return { 22 | Label: { default: label }, 23 | Parameters: params.map(p => { 24 | return p ? p.logicalId : ''; 25 | }).filter(id => id), 26 | }; 27 | }; 28 | this.templateOptions.metadata['AWS::CloudFormation::Interface'] = { 29 | ParameterGroups: Object.keys(this._paramGroup).map(key => mkgrp(key, this._paramGroup[key]) ), 30 | }; 31 | } 32 | } 33 | 34 | interface KeycloakStackProps extends StackProps { 35 | readonly auroraServerlessV2?: boolean; 36 | readonly fromExistingVPC?: boolean; 37 | } 38 | 39 | interface KeycloakSettings { 40 | certificateArn: string; 41 | hostname: string; 42 | vpc?: ec2.IVpc; 43 | publicSubnets?: ec2.SubnetSelection; 44 | privateSubnets?: ec2.SubnetSelection; 45 | databaseSubnets?: ec2.SubnetSelection; 46 | databaseInstanceType?: ec2.InstanceType; 47 | taskCpu?: number; 48 | taskMemory?: number; 49 | } 50 | 51 | export class KeycloakStack extends SolutionStack { 52 | private _keycloakSettings: KeycloakSettings = { certificateArn: '', hostname: '' }; 53 | 54 | constructor(scope: Construct, id: string, props: KeycloakStackProps = {}) { 55 | super(scope, id, props); 56 | 57 | const dbMsg = props.auroraServerlessV2 ? 'using aurora serverless v2' : 'rds mysql'; 58 | const vpcMsg = props.fromExistingVPC ? 'existing vpc' : 'new vpc'; 59 | 60 | this.setDescription(`(SO8021) - Deploy keycloak ${dbMsg} with ${vpcMsg}. template version: ${process.env.VERSION}`); 61 | 62 | const certificateArnParam = this.makeParam('CertificateArn', { 63 | type: 'String', 64 | description: 'Certificate Arn for Application Load Balancer', 65 | minLength: 5, 66 | }); 67 | 68 | this.addGroupParam({ 'Application Load Balancer Settings': [certificateArnParam] }); 69 | 70 | this._keycloakSettings.certificateArn = certificateArnParam.valueAsString; 71 | 72 | const hostnameParam = this.makeParam('Hostname', { 73 | type: 'String', 74 | description: 'Hostname for Keycloak server', 75 | minLength: 5, 76 | }); 77 | 78 | this.addGroupParam({ 'Keycloak Hostname Settings': [hostnameParam] }); 79 | 80 | this._keycloakSettings.hostname = hostnameParam.valueAsString; 81 | 82 | if (!props.auroraServerlessV2) { 83 | const databaseInstanceType = this.makeParam('DatabaseInstanceType', { 84 | type: 'String', 85 | description: 'Instance type to be used for the core instances', 86 | allowedValues: INSTANCE_TYPES, 87 | default: 'r5.large', 88 | }); 89 | this.addGroupParam({ 'Database Instance Settings': [databaseInstanceType] }); 90 | this._keycloakSettings.databaseInstanceType = new ec2.InstanceType(databaseInstanceType.valueAsString); 91 | } 92 | 93 | if (props.fromExistingVPC) { 94 | const vpcIdParam = this.makeParam('VpcId', { 95 | type: 'AWS::EC2::VPC::Id', 96 | description: 'Your VPC Id', 97 | }); 98 | const pubSubnetsParam = this.makeParam('PubSubnets', { 99 | type: 'List', 100 | description: 'Public subnets (Choose two)', 101 | }); 102 | const privSubnetsParam = this.makeParam('PrivSubnets', { 103 | type: 'List', 104 | description: 'Private subnets (Choose two)', 105 | }); 106 | const dbSubnetsParam = this.makeParam('DBSubnets', { 107 | type: 'List', 108 | description: 'Database subnets (Choose two)', 109 | }); 110 | this.addGroupParam({ 'VPC Settings': [vpcIdParam, pubSubnetsParam, privSubnetsParam, dbSubnetsParam] }); 111 | 112 | const azs = ['a', 'b']; 113 | const vpc = ec2.Vpc.fromVpcAttributes(this, 'VpcAttr', { 114 | vpcId: vpcIdParam.valueAsString, 115 | vpcCidrBlock: Aws.NO_VALUE, 116 | availabilityZones: azs, 117 | publicSubnetIds: azs.map((_, index) => Fn.select(index, pubSubnetsParam.valueAsList)), 118 | privateSubnetIds: azs.map((_, index) => Fn.select(index, privSubnetsParam.valueAsList)), 119 | isolatedSubnetIds: azs.map((_, index) => Fn.select(index, dbSubnetsParam.valueAsList)), 120 | }); 121 | 122 | Object.assign(this._keycloakSettings, { 123 | vpc, 124 | publicSubnets: { subnets: vpc.publicSubnets }, 125 | privateSubnets: { subnets: vpc.privateSubnets }, 126 | databaseSubnets: { subnets: vpc.isolatedSubnets }, 127 | }); 128 | } 129 | 130 | const taskCpu = this.makeParam('TaskCPU', { 131 | type: 'Number', 132 | description: 'Specify the amount of CPU to reserve for your keycloak task.', 133 | allowedValues: TASK_CPU_OPTIONS, 134 | default: 4096, 135 | }); 136 | const taskMemory = this.makeParam('TaskMemory', { 137 | type: 'Number', 138 | description: 'Specify the amount of memory to reserve for your keycloak task. Please confirm the memory you select is compatible with the TaskCPU: https://docs.aws.amazon.com/AmazonECS/latest/userguide/fargate-task-defs.html#fargate-tasks-size ', 139 | allowedValues: TASK_MEMORY_OPTIONS, 140 | default: 8192, 141 | }); 142 | this.addGroupParam({ 'Fargate Task Size Settings': [taskCpu, taskMemory] }); 143 | this._keycloakSettings.taskCpu = taskCpu.valueAsNumber; 144 | this._keycloakSettings.taskMemory = taskMemory.valueAsNumber; 145 | 146 | const minContainersParam = this.makeParam('MinContainers', { 147 | type: 'Number', 148 | description: 'minimum containers count', 149 | default: 2, 150 | minValue: 2, 151 | }); 152 | const maxContainersParam = this.makeParam('MaxContainers', { 153 | type: 'Number', 154 | description: 'maximum containers count', 155 | default: 10, 156 | minValue: 2, 157 | }); 158 | const targetCpuUtilizationParam = this.makeParam('AutoScalingTargetCpuUtilization', { 159 | type: 'Number', 160 | description: 'Auto scaling target cpu utilization', 161 | default: 75, 162 | minValue: 0, 163 | }); 164 | this.addGroupParam({ 'AutoScaling Settings': [minContainersParam, maxContainersParam, targetCpuUtilizationParam] }); 165 | 166 | const javaOptsParam = this.makeParam('JavaOpts', { 167 | type: 'String', 168 | description: 'JAVA_OPTS environment variable', 169 | default: '-server -Xms1024m -Xmx1638m' 170 | }); 171 | this.addGroupParam({ 'Environment variable': [javaOptsParam] }); 172 | 173 | new KeyCloak(this, 'KeyCloak', { 174 | vpc: this._keycloakSettings.vpc, 175 | publicSubnets: this._keycloakSettings.publicSubnets, 176 | privateSubnets: this._keycloakSettings.privateSubnets, 177 | databaseSubnets: this._keycloakSettings.databaseSubnets, 178 | certificateArn: this._keycloakSettings.certificateArn, 179 | auroraServerlessV2: props.auroraServerlessV2, 180 | databaseInstanceType: this._keycloakSettings.databaseInstanceType, 181 | stickinessCookieDuration: Duration.days(7), 182 | nodeCount: minContainersParam.valueAsNumber, 183 | taskCpu: this._keycloakSettings.taskCpu, 184 | taskMemory: this._keycloakSettings.taskMemory, 185 | autoScaleTask: { 186 | min: minContainersParam.valueAsNumber, 187 | max: maxContainersParam.valueAsNumber, 188 | targetCpuUtilization: targetCpuUtilizationParam.valueAsNumber, 189 | }, 190 | env: { 191 | JAVA_OPTS: javaOptsParam.valueAsString, 192 | }, 193 | keycloakVersion: KeycloakVersion.of('22.0.4'), 194 | hostname: this._keycloakSettings.hostname, 195 | }); 196 | } 197 | 198 | } 199 | 200 | const INSTANCE_TYPES = [ 201 | 'r5.large', 202 | 'r5.xlarge', 203 | 'r5.2xlarge', 204 | 'r5.4xlarge', 205 | 'r5.8xlarge', 206 | 'r5.12xlarge', 207 | 'r5.16xlarge', 208 | 'r5.24xlarge', 209 | 't3.small', 210 | 't3.medium' 211 | ]; 212 | 213 | const TASK_CPU_OPTIONS = ['1024', '2048', '4096']; 214 | const TASK_MEMORY_OPTIONS = ['2048', '3072', '4096', '5120', '6144', '7168', '8192', '9216', '10240', '11264', '12288', '13312', '14336', '15360', '16384', '17408', '18432', '19456', '20480', '21504', '22528', '23552', '24576', '25600', '26624', '27648', '28672', '29696', '30720']; -------------------------------------------------------------------------------- /source/test/main.test.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'aws-cdk-lib'; 2 | import { Template } from 'aws-cdk-lib/assertions'; 3 | import { KeycloakStack } from '../src/stack'; 4 | 5 | test('Snapshot', () => { 6 | const app = new App(); 7 | const stack = new KeycloakStack(app, 'test', { fromExistingVPC: true }); 8 | const template = Template.fromStack(stack); 9 | 10 | template.resourceCountIs('AWS::S3::Bucket', 0); 11 | expect(template).toMatchSnapshot(); 12 | }); -------------------------------------------------------------------------------- /source/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "inlineSourceMap": true, 7 | "inlineSources": true, 8 | "lib": [ 9 | "es2018" 10 | ], 11 | "module": "CommonJS", 12 | "noEmitOnError": false, 13 | "noFallthroughCasesInSwitch": true, 14 | "noImplicitAny": true, 15 | "noImplicitReturns": true, 16 | "noImplicitThis": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "resolveJsonModule": true, 20 | "strict": true, 21 | "strictNullChecks": true, 22 | "strictPropertyInitialization": true, 23 | "stripInternal": true, 24 | "target": "ES2018" 25 | }, 26 | "include": [ 27 | "src/**/*.ts", 28 | "test/**/*.ts" 29 | ], 30 | "exclude": [ 31 | "node_modules" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /source/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "src", 4 | "outDir": "lib", 5 | "alwaysStrict": true, 6 | "declaration": true, 7 | "experimentalDecorators": true, 8 | "inlineSourceMap": true, 9 | "inlineSources": true, 10 | "lib": [ 11 | "es2018" 12 | ], 13 | "module": "CommonJS", 14 | "noEmitOnError": false, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitAny": true, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "resolveJsonModule": true, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "strictPropertyInitialization": true, 25 | "stripInternal": true, 26 | "target": "ES2018" 27 | }, 28 | "include": [ 29 | "src/**/*.ts" 30 | ], 31 | "exclude": [ 32 | "node_modules", 33 | "lib", 34 | "cdk.out" 35 | ] 36 | } -------------------------------------------------------------------------------- /source/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.1.0" 3 | } 4 | -------------------------------------------------------------------------------- /tutorials/api-gateway/resources/keycloak.json: -------------------------------------------------------------------------------- 1 | { 2 | "realm": "keycloak-on-aws", 3 | "auth-server-url": "https://keycloak.yourdomain.com/auth/", 4 | "ssl-required": "external", 5 | "resource": "vue", 6 | "public-client": true, 7 | "confidential-port": 0, 8 | "x-api-gw-url": "http://localhost:3003/dev/hello" 9 | } 10 | -------------------------------------------------------------------------------- /tutorials/api-gateway/resources/policyAllowDocument.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "Stmt1517409086717", 6 | "Action": "*", 7 | "Effect": "Allow", 8 | "Resource": "*" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tutorials/api-gateway/resources/policyDenyDocument.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "Stmt1517409086717", 6 | "Action": "*", 7 | "Effect": "Deny", 8 | "Resource": "*" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/.gitignore: -------------------------------------------------------------------------------- 1 | .serverless/ 2 | .webpack/ 3 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/api-gateway-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "resource": "/{proxy+}", 3 | "path": "/users", 4 | "httpMethod": "POST", 5 | "headers": { 6 | "Accept": "*/*", 7 | "Accept-Encoding": "gzip, deflate, br", 8 | "Accept-Language": "en-US,en;q=0.9", 9 | "cache-control": "no-cache", 10 | "CloudFront-Forwarded-Proto": "https", 11 | "CloudFront-Is-Desktop-Viewer": "true", 12 | "CloudFront-Is-Mobile-Viewer": "false", 13 | "CloudFront-Is-SmartTV-Viewer": "false", 14 | "CloudFront-Is-Tablet-Viewer": "false", 15 | "CloudFront-Viewer-Country": "US", 16 | "content-type": "application/json", 17 | "Host": "xxxxxx.execute-api.us-east-1.amazonaws.com", 18 | "origin": "https://xxxxxx.execute-api.us-east-1.amazonaws.com", 19 | "pragma": "no-cache", 20 | "Referer": "https://xxxxxx.execute-api.us-east-1.amazonaws.com/prod/", 21 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36", 22 | "Via": "2.0 00f0a41f749793b9dd653153037c957e.cloudfront.net (CloudFront)", 23 | "X-Amz-Cf-Id": "2D5N65SYHJdnJfEmAV_hC0Mw3QvkbUXDumJKAL786IGHRdq_MggPtA==", 24 | "X-Amzn-Trace-Id": "Root=1-5cdf30d0-31a428004abe13807f9445b0", 25 | "X-Forwarded-For": "11.111.111.111, 11.111.111.111", 26 | "X-Forwarded-Port": "443", 27 | "X-Forwarded-Proto": "https" 28 | }, 29 | "multiValueHeaders": { 30 | "Accept": [ 31 | "*/*" 32 | ], 33 | "Accept-Encoding": [ 34 | "gzip, deflate, br" 35 | ], 36 | "Accept-Language": [ 37 | "en-US,en;q=0.9" 38 | ], 39 | "cache-control": [ 40 | "no-cache" 41 | ], 42 | "CloudFront-Forwarded-Proto": [ 43 | "https" 44 | ], 45 | "CloudFront-Is-Desktop-Viewer": [ 46 | "true" 47 | ], 48 | "CloudFront-Is-Mobile-Viewer": [ 49 | "false" 50 | ], 51 | "CloudFront-Is-SmartTV-Viewer": [ 52 | "false" 53 | ], 54 | "CloudFront-Is-Tablet-Viewer": [ 55 | "false" 56 | ], 57 | "CloudFront-Viewer-Country": [ 58 | "US" 59 | ], 60 | "content-type": [ 61 | "application/json" 62 | ], 63 | "Host": [ 64 | "xxxxxx.execute-api.us-east-1.amazonaws.com" 65 | ], 66 | "origin": [ 67 | "https://xxxxxx.execute-api.us-east-1.amazonaws.com" 68 | ], 69 | "pragma": [ 70 | "no-cache" 71 | ], 72 | "Referer": [ 73 | "https://xxxxxx.execute-api.us-east-1.amazonaws.com/prod/" 74 | ], 75 | "User-Agent": [ 76 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" 77 | ], 78 | "Via": [ 79 | "2.0 00f0a41f749793b9dd653153037c957e.cloudfront.net (CloudFront)" 80 | ], 81 | "X-Amz-Cf-Id": [ 82 | "2D5N65SYHJdnJfEmAV_hC0Mw3QvkbUXDumJKAL786IGHRdq_MggPtA==" 83 | ], 84 | "X-Amzn-Trace-Id": [ 85 | "Root=1-5cdf30d0-31a428004abe13807f9445b0" 86 | ], 87 | "X-Forwarded-For": [ 88 | "11.111.111.111, 11.111.111.111" 89 | ], 90 | "X-Forwarded-Port": [ 91 | "443" 92 | ], 93 | "X-Forwarded-Proto": [ 94 | "https" 95 | ] 96 | }, 97 | "queryStringParameters": null, 98 | "multiValueQueryStringParameters": null, 99 | "pathParameters": { 100 | "proxy": "users" 101 | }, 102 | "stageVariables": {}, 103 | "requestContext": { 104 | "resourceId": "xxxxx", 105 | "resourcePath": "/{proxy+}", 106 | "httpMethod": "POST", 107 | "extendedRequestId": "Z2SQlEORIAMFjpA=", 108 | "requestTime": "17/May/2019:22:08:16 +0000", 109 | "path": "/prod/users", 110 | "accountId": "xxxxxxxx", 111 | "protocol": "HTTP/1.1", 112 | "stage": "prod", 113 | "domainPrefix": "xxxxxx", 114 | "requestTimeEpoch": 1558130896565, 115 | "requestId": "4589cf16-78f0-11e9-9c65-816a9b037cec", 116 | "identity": { 117 | "cognitoIdentityPoolId": null, 118 | "accountId": null, 119 | "cognitoIdentityId": null, 120 | "caller": null, 121 | "sourceIp": "11.111.111.111", 122 | "principalOrgId": null, 123 | "accessKey": null, 124 | "cognitoAuthenticationType": null, 125 | "cognitoAuthenticationProvider": null, 126 | "userArn": null, 127 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36", 128 | "user": null 129 | }, 130 | "domainName": "xxxxxx.execute-api.us-east-1.amazonaws.com", 131 | "apiId": "xxxxxx" 132 | }, 133 | "body": "{\"name\": \"Sandy Samantha Salamander\"}", 134 | "isBase64Encoded": false 135 | } -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/app.local.js: -------------------------------------------------------------------------------- 1 | const app = require('./src/app') 2 | const port = 3000 3 | 4 | app.listen(port) 5 | console.info(`listening on http://localhost:${port}`) 6 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/event.js: -------------------------------------------------------------------------------- 1 | 2 | // AUTH 3 | { 4 | event: { 5 | enhancedAuthContext: {}, 6 | methodArn: 'arn:aws:execute-api:us-east-1:random-account-id:random-api-id/dev/GET/hello', 7 | requestContext: { 8 | accountId: 'random-account-id', 9 | apiId: 'random-api-id', 10 | httpMethod: 'GET', 11 | requestId: 'random-request-id', 12 | resourceId: 'random-resource-id', 13 | resourcePath: '/hello', 14 | path: '/dev/hello', 15 | stage: 'dev' 16 | }, 17 | resource: '/hello', 18 | authorizationToken: undefined, 19 | type: 'TOKEN' 20 | }, 21 | context: { 22 | awsRequestId: 'cklxefdo70000qiunei52ade3', 23 | callbackWaitsForEmptyEventLoop: true, 24 | clientContext: null, 25 | functionName: 'serverless-express-api-gw-dev-authEndpoint', 26 | functionVersion: '$LATEST', 27 | identity: undefined, 28 | invokedFunctionArn: 'offline_invokedFunctionArn_for_serverless-express-api-gw-dev-authEndpoint', 29 | logGroupName: 'offline_logGroupName_for_serverless-express-api-gw-dev-authEndpoint', 30 | logStreamName: 'offline_logStreamName_for_serverless-express-api-gw-dev-authEndpoint', 31 | memoryLimitInMB: '1024', 32 | getRemainingTimeInMillis: [Function: getRemainingTimeInMillis], 33 | done: [Function: done], 34 | fail: [Function: fail], 35 | succeed: [Function: succeed] 36 | }, 37 | log: { 38 | error: [Function: error], 39 | warn: [Function: warn], 40 | info: [Function: info], 41 | verbose: [Function: verbose], 42 | debug: [Function: debug] 43 | } 44 | } 45 | 46 | // NORMAL 47 | { 48 | event: { 49 | body: null, 50 | headers: { 51 | Host: '0.0.0.0:3003', 52 | 'User-Agent': 'curl/7.64.1', 53 | Accept: '*/*' 54 | }, 55 | httpMethod: 'GET', 56 | isBase64Encoded: false, 57 | multiValueHeaders: { Host: [Array], 'User-Agent': [Array], Accept: [Array] }, 58 | multiValueQueryStringParameters: null, 59 | path: '/hello', 60 | pathParameters: null, 61 | queryStringParameters: null, 62 | requestContext: { 63 | accountId: 'offlineContext_accountId', 64 | apiId: 'offlineContext_apiId', 65 | authorizer: [Object], 66 | domainName: 'offlineContext_domainName', 67 | domainPrefix: 'offlineContext_domainPrefix', 68 | extendedRequestId: 'cklxe3ubu0000obunadrs7owx', 69 | httpMethod: 'GET', 70 | identity: [Object], 71 | path: '/hello', 72 | protocol: 'HTTP/1.1', 73 | requestId: 'cklxe3ubu0001obun8qkjcq2i', 74 | requestTime: '06/Mar/2021:15:13:08 +0800', 75 | requestTimeEpoch: 1615014788675, 76 | resourceId: 'offlineContext_resourceId', 77 | resourcePath: '/dev/hello', 78 | stage: 'dev' 79 | }, 80 | resource: '/dev/hello', 81 | stageVariables: undefined 82 | }, 83 | context: { 84 | awsRequestId: 'cklxe3ubw0002obunax1q97ca', 85 | callbackWaitsForEmptyEventLoop: true, 86 | clientContext: null, 87 | functionName: 'serverless-express-api-gw-dev-hello', 88 | functionVersion: '$LATEST', 89 | identity: undefined, 90 | invokedFunctionArn: 'offline_invokedFunctionArn_for_serverless-express-api-gw-dev-hello', 91 | logGroupName: 'offline_logGroupName_for_serverless-express-api-gw-dev-hello', 92 | logStreamName: 'offline_logStreamName_for_serverless-express-api-gw-dev-hello', 93 | memoryLimitInMB: '1024', 94 | getRemainingTimeInMillis: [Function: getRemainingTimeInMillis], 95 | done: [Function: done], 96 | fail: [Function: fail], 97 | succeed: [Function: succeed] 98 | }, 99 | log: { 100 | error: [Function: error], 101 | warn: [Function: warn], 102 | info: [Function: info], 103 | verbose: [Function: verbose], 104 | debug: [Function: debug] 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-express-auth", 3 | "version": "2.1.1", 4 | "description": "Example application for running a Node Express app on AWS Lambda using Amazon API Gateway.", 5 | "private": true, 6 | "main": "src/lambda.js", 7 | "config": { 8 | "s3BucketName": "sls-xpress-edge1", 9 | "region": "us-east-1", 10 | "cloudFormationStackName": "ServerlessExpressStack", 11 | "functionName": "", 12 | "domain": "" 13 | }, 14 | "scripts": { 15 | "start": "node app.local.js", 16 | "local": "node scripts/local", 17 | "offline": "SLS_DEBUG=* serverless offline --host 0.0.0.0 --httpPort 3003 --lambdaPort=3019", 18 | "deploy": "cross-var aws cloudformation deploy --template-file sam-template.packaged.yaml --stack-name $npm_package_config_cloudFormationStackName --capabilities CAPABILITY_IAM --region $npm_package_config_region --parameter-overrides DomainName=$npm_package_config_domain", 19 | "package-deploy": "npm run build && npm run package && npm run deploy", 20 | "build": "SLS_DEBUG=* NODE_ENV=production sls webpack", 21 | "build:dev": "SLS_DEBUG=* sls webpack" 22 | }, 23 | "license": "Apache-2.0", 24 | "dependencies": { 25 | "@vendia/serverless-express": "^4.3.0", 26 | "body-parser": "^1.17.1", 27 | "compression": "^1.6.2", 28 | "cors": "^2.8.3", 29 | "express": "^4.15.2", 30 | "keycloak-connect": "^21.0.1" 31 | }, 32 | "devDependencies": { 33 | "copy-webpack-plugin": "^7.0.0", 34 | "cross-var": "^1.1.0", 35 | "serverless": "3.22.0", 36 | "serverless-offline": "10.0.1", 37 | "serverless-webpack": "^5.3.5", 38 | "source-map-support": "^0.5.19", 39 | "webpack": "^5.76.0", 40 | "webpack-cli": "^4.5.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/scripts/local.js: -------------------------------------------------------------------------------- 1 | const lambdaFunction = require('../src/lambda.js') 2 | const apiGatewayEvent = require('../api-gateway-event.json') 3 | 4 | const context = { 5 | succeed: v => { 6 | console.info(v) 7 | process.exit(0) 8 | } 9 | } 10 | const server = lambdaFunction.handler(apiGatewayEvent, context).then((e, v) => { 11 | if (e) console.error(e) 12 | if (v) console.info(v) 13 | process.exit(0) 14 | }) 15 | 16 | process.stdin.resume() 17 | 18 | function exitHandler (options, err) { 19 | if (options.cleanup && server && server.close) { 20 | server.close() 21 | } 22 | 23 | if (err) console.error(err.stack) 24 | if (options.exit) process.exit() 25 | } 26 | 27 | process.on('exit', exitHandler.bind(null, { cleanup: true })) 28 | process.on('SIGINT', exitHandler.bind(null, { exit: true })) // ctrl+c event 29 | process.on('SIGTSTP', exitHandler.bind(null, { exit: true })) // ctrl+v event 30 | process.on('uncaughtException', exitHandler.bind(null, { exit: true })) 31 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/serverless.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Serverless! 2 | # 3 | # This file is the main config file for your service. 4 | # It's very minimal at this point and uses default values. 5 | # You can always add more config options for more control. 6 | # We've included some commented out config examples here. 7 | # Just uncomment any of them to get that config option. 8 | # 9 | # For full config options, check the docs: 10 | # docs.serverless.com 11 | # 12 | # Happy Coding! 13 | 14 | service: serverless-express-auth 15 | # app and org for use with dashboard.serverless.com 16 | #app: your-app-name 17 | #org: your-org-name 18 | 19 | # You can pin your service to only deploy with a specific Serverless version 20 | # Check out our docs for more details 21 | frameworkVersion: ^3.22.0 22 | custom: 23 | webpack: 24 | webpackConfig: webpack.config.js 25 | 26 | 27 | plugins: 28 | - serverless-webpack 29 | - serverless-offline 30 | provider: 31 | name: aws 32 | runtime: nodejs12.x 33 | region: us-east-1 34 | timeout: 30 35 | 36 | 37 | functions: 38 | authEndpoint: 39 | handler: src/lambda.auth 40 | hello: 41 | handler: src/lambda.hello 42 | events: 43 | - http: 44 | path: hello 45 | method: GET 46 | cors: true 47 | authorizer: authEndpoint 48 | resources: 49 | Resources: 50 | GatewayResponse: 51 | Type: 'AWS::ApiGateway::GatewayResponse' 52 | Properties: 53 | ResponseParameters: 54 | gatewayresponse.header.Access-Control-Allow-Origin: "'*'" 55 | gatewayresponse.header.Access-Control-Allow-Headers: "'*'" 56 | ResponseType: EXPIRED_TOKEN 57 | RestApiId: 58 | Ref: 'ApiGatewayRestApi' 59 | StatusCode: '401' 60 | AuthFailureGatewayResponse: 61 | Type: 'AWS::ApiGateway::GatewayResponse' 62 | Properties: 63 | ResponseParameters: 64 | gatewayresponse.header.Access-Control-Allow-Origin: "'*'" 65 | gatewayresponse.header.Access-Control-Allow-Headers: "'*'" 66 | ResponseType: UNAUTHORIZED 67 | RestApiId: 68 | Ref: 'ApiGatewayRestApi' 69 | StatusCode: '401' 70 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/src/app.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const express = require('express') 3 | const Keycloak = require('keycloak-connect') 4 | const app = express() 5 | 6 | const ALLOW_POLICY = jsonReadSync(`${__dirname}/../policyAllowDocument.json`) 7 | const DENY_POLICY = jsonReadSync(`${__dirname}/../policyDenyDocument.json`) 8 | 9 | const keycloak = new Keycloak({}, Object.assign( 10 | jsonReadSync(`${__dirname}/../keycloak.json`), 11 | { 12 | 'bearer-only': true, // https://www.keycloak.org/docs/latest/securing_apps/ 13 | } 14 | )) 15 | 16 | app.use(keycloak.middleware()) 17 | 18 | // https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/nodejs-adapter.adoc#protecting-resources 19 | app.all('*', keycloak.protect('realm:call-api'), (req, res) => { 20 | const content = req.kauth.grant.access_token.content 21 | res.json({ 22 | principalId: content.email || content.preferred_username, 23 | policyDocument: ALLOW_POLICY, 24 | context: { 25 | name: content.name 26 | }, 27 | }) 28 | }) 29 | 30 | module.exports = app 31 | 32 | function jsonReadSync(path) { 33 | try { 34 | return JSON.parse(fs.readFileSync(path)) 35 | } catch (err) { 36 | console.error(err) 37 | } 38 | return {} 39 | } 40 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/src/lambda.js: -------------------------------------------------------------------------------- 1 | require('source-map-support/register') 2 | const serverlessExpress = require('@vendia/serverless-express') 3 | const app = require('./app') 4 | 5 | exports.auth = function (event, context, callback) { 6 | Object.assign(event, { 7 | path: event.path || event.requestContext.path, 8 | headers: Object.assign(event.headers || {}, { authorization: event.authorizationToken }), 9 | httpMethod: event.httpMethod || event.requestContext.httpMethod 10 | }) 11 | const handler = serverlessExpress({ 12 | app, 13 | logSettings: { level: 'debug' }, 14 | // TODO: Can't use `eventSource` override `getResponse` only, must override both `getRequest` and `getResponse`. 15 | // Will send a PR to the up stream to fix that. 16 | }) 17 | handler(event, context, callback) 18 | .then((res) => { 19 | callback(null, JSON.parse(res.body)) 20 | }) 21 | .catch((err) => { 22 | console.error('Authentication error', err) 23 | callback('Unauthorized') 24 | }) 25 | } 26 | 27 | exports.hello = function (event, context, callback) { 28 | callback(null, { 29 | statusCode: 200, 30 | body: JSON.stringify( 31 | { 32 | message: 'Hello World from protect server', 33 | }, 34 | ), 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-express-auth/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path') 3 | const slsw = require('serverless-webpack') 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | 6 | const env = process.env.NODE_ENV === 'production' ? 'production' : 'development'; 7 | 8 | module.exports = { 9 | entry: slsw.lib.entries, 10 | target: 'node', 11 | mode: env, 12 | devtool: 'source-map', 13 | output: { 14 | libraryTarget: 'commonjs', 15 | path: path.join(__dirname, '.webpack'), 16 | filename: '[name].js', 17 | }, 18 | plugins: [ 19 | new webpack.DefinePlugin({ 20 | '.': '__dirname', 21 | }), 22 | new CopyWebpackPlugin({ 23 | patterns: [ 24 | { 25 | from: path.join(__dirname, '../resources'), 26 | to: '.', 27 | }, 28 | ], 29 | }), 30 | ], 31 | } 32 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-lambda-auth/.gitignore: -------------------------------------------------------------------------------- 1 | .serverless/ 2 | .webpack/ 3 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-lambda-auth/authorizerUtil.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | function getPolicyDocument(name) { 4 | try { 5 | return JSON.parse(fs.readFileSync(`${__dirname}/policy${name}Document.json`, 'utf8')); 6 | } catch (e) { 7 | if (e.code === 'ENOENT') { 8 | console.error(`Expected policy${name}Document.json to be included in Lambda deployment package`); 9 | // fallthrough 10 | } 11 | throw e; 12 | } 13 | } 14 | 15 | // extract user_id from the autho0 userInfo and return it for AWS principalId 16 | async function getPrincipalId(userInfo) { 17 | if (!userInfo || (!userInfo.email && !userInfo.preferred_username)) { 18 | throw new Error('No email returned from authentication service'); 19 | } 20 | console.info(`authentication successful for user ${userInfo.email || userInfo.preferred_username}`); 21 | 22 | return userInfo.email || userInfo.preferred_username; 23 | } 24 | 25 | async function getUserInfo(jwt) { 26 | if (!jwt) { 27 | throw new Error('data empty return'); 28 | } 29 | if (jwt === 'Unauthorized') { 30 | throw new Error('Unauthorized'); 31 | } 32 | const user = {}; 33 | user.name = jwt.name; 34 | user.email = jwt.email; 35 | user.preferred_username = jwt.preferred_username; 36 | user.given_name = jwt.given_name; 37 | user.family_name = jwt.family_name; 38 | const principalId = await getPrincipalId(jwt); 39 | if (!principalId) { 40 | return null; 41 | } 42 | user.principalId = principalId; 43 | return user; 44 | } 45 | 46 | export async function getAuthentication(jwt, name) { 47 | const userInfo = await getUserInfo(jwt); 48 | return { 49 | principalId: userInfo.principalId, 50 | policyDocument: getPolicyDocument(name), 51 | context: userInfo, 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-lambda-auth/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | 4 | const presets = [ 5 | [ 6 | '@babel/preset-env', 7 | { 8 | targets: { 9 | esmodules: true, 10 | }, 11 | }, 12 | ], 13 | ]; 14 | const plugins = [ 15 | ['@babel/transform-runtime', { 16 | helpers: false, 17 | regenerator: true, 18 | }], 19 | ['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }], 20 | ['@babel/plugin-proposal-object-rest-spread'], 21 | ['@babel/plugin-transform-async-to-generator'], 22 | ]; 23 | 24 | if (process.env.NODE_ENV === 'test') { 25 | plugins.push('babel-plugin-dynamic-import-node'); 26 | } else { 27 | plugins.push('@babel/plugin-syntax-dynamic-import'); 28 | } 29 | 30 | return { 31 | presets, 32 | plugins, 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-lambda-auth/handler.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import jsonwebtoken from 'jsonwebtoken'; 3 | import { apigateway } from 'keycloak-lambda-authorizer'; 4 | import { getAuthentication } from './authorizerUtil'; 5 | 6 | function getKeycloakJSON() { 7 | return new Promise(((resolve, reject) => { 8 | fs.readFile(`${__dirname}/keycloak.json`, 'utf8', (err, data) => { 9 | if (err) reject(err); 10 | else resolve(JSON.parse(data)); 11 | }); 12 | })); 13 | } 14 | 15 | function getToken(event) { 16 | const tokenString = event.authorizationToken || event.headers.Authorization; 17 | if (!tokenString) { 18 | throw new Error('Expected \'event.authorizationToken\' parameter to be set'); 19 | } 20 | const match = tokenString.match(/^Bearer (.*)$/); 21 | if (!match || match.length < 2) { 22 | throw new Error(`Invalid Authorization token - '${tokenString}' does not match 'Bearer .*'`); 23 | } 24 | const tokenStringValue = match[1]; 25 | return jsonwebtoken.decode(tokenStringValue); 26 | } 27 | 28 | export function hello(event, context, callback) { 29 | const token = getToken(event); 30 | callback(null, { 31 | statusCode: 200, 32 | body: JSON.stringify( 33 | { 34 | message: `Hi ${token.preferred_username}. Your function executed successfully!`, 35 | }, 36 | ), 37 | }); 38 | } 39 | 40 | export async function auth0(event) { 41 | const keycloakJSON = await getKeycloakJSON(); 42 | const token = await apigateway.awsAdapter(event, keycloakJSON, { 43 | enforce: { 44 | enabled: true, 45 | role: 'call-api', 46 | }, 47 | }); 48 | return token.payload; 49 | } 50 | 51 | function getDecodedToken(event) { 52 | try { 53 | return getToken(event); 54 | } catch (e) { 55 | return null; 56 | } 57 | } 58 | 59 | export function auth(event, context, callback) { 60 | const token = getDecodedToken(event); 61 | if (token) { 62 | auth0(event).then((jwt) => { 63 | getAuthentication(jwt, 'Allow') 64 | .then((res) => { 65 | callback(null, res); 66 | }) 67 | .catch((e) => { 68 | console.error('getAuthentication error', e); 69 | callback('Unauthorized'); 70 | }); 71 | }).catch((e) => { 72 | console.error('auth0 error', e); 73 | getAuthentication(token, 'Deny') 74 | .then((res) => { 75 | callback(null, res); 76 | }) 77 | .catch((e1) => { 78 | console.error('getAuthentication error', e1); 79 | callback('Unauthorized'); 80 | }); 81 | }); 82 | } else { 83 | callback('Unauthorized'); // Invalid Token. 401 error 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-lambda-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-lambda-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "handler.js", 6 | "private": true, 7 | "scripts": { 8 | "build": "SLS_DEBUG=* NODE_ENV=production sls webpack", 9 | "build:dev": "SLS_DEBUG=* sls webpack", 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "offline": "SLS_DEBUG=* serverless offline --host 0.0.0.0 --httpPort 3003 --lambdaPort=3019", 12 | "deploy": "serverless deploy --aws-profile us-east-1", 13 | "lint": "eslint --quiet --ext .js handler.js", 14 | "lint:fix": "eslint --fix --quiet --ext .js handler.js" 15 | }, 16 | "license": "Apache-2.0", 17 | "devDependencies": { 18 | "@babel/core": "^7.13.8", 19 | "@babel/plugin-proposal-decorators": "^7.13.5", 20 | "@babel/plugin-proposal-object-rest-spread": "^7.13.8", 21 | "@babel/plugin-transform-async-to-generator": "^7.13.0", 22 | "@babel/plugin-transform-runtime": "^7.13.8", 23 | "@babel/preset-env": "^7.13.8", 24 | "@babel/register": "^7.13.8", 25 | "@babel/runtime": "^7.13.8", 26 | "babel-loader": "^8.2.2", 27 | "copy-webpack-plugin": "*", 28 | "eslint": "*", 29 | "eslint-config-airbnb": "*", 30 | "eslint-plugin-import": "*", 31 | "progress-bar-webpack-plugin": "^2.1.0", 32 | "serverless": "3.22.0", 33 | "serverless-offline": "10.0.1", 34 | "serverless-webpack": "^5.3.5", 35 | "webpack": "^5.76.0", 36 | "webpack-cli": "^4.5.0" 37 | }, 38 | "dependencies": { 39 | "keycloak-lambda-authorizer": "^1.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-lambda-auth/serverless.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Serverless! 2 | # 3 | # This file is the main config file for your service. 4 | # It's very minimal at this point and uses default values. 5 | # You can always add more config options for more control. 6 | # We've included some commented out config examples here. 7 | # Just uncomment any of them to get that config option. 8 | # 9 | # For full config options, check the docs: 10 | # docs.serverless.com 11 | # 12 | # Happy Coding! 13 | 14 | service: serverless-lambda-auth 15 | # app and org for use with dashboard.serverless.com 16 | #app: your-app-name 17 | #org: your-org-name 18 | 19 | # You can pin your service to only deploy with a specific Serverless version 20 | # Check out our docs for more details 21 | frameworkVersion: ^3.22.0 22 | custom: 23 | babelPresets: 24 | - latest 25 | webpack: 26 | webpackConfig: webpack.config.babel.js 27 | 28 | 29 | plugins: 30 | - serverless-webpack 31 | - serverless-offline 32 | provider: 33 | name: aws 34 | runtime: nodejs12.x 35 | region: us-east-1 36 | timeout: 30 37 | 38 | 39 | functions: 40 | authEndpoint: 41 | handler: handler.auth 42 | hello: 43 | handler: handler.hello 44 | events: 45 | - http: 46 | path: hello 47 | method: GET 48 | cors: true 49 | authorizer: authEndpoint 50 | resources: 51 | Resources: 52 | GatewayResponse: 53 | Type: 'AWS::ApiGateway::GatewayResponse' 54 | Properties: 55 | ResponseParameters: 56 | gatewayresponse.header.Access-Control-Allow-Origin: "'*'" 57 | gatewayresponse.header.Access-Control-Allow-Headers: "'*'" 58 | ResponseType: EXPIRED_TOKEN 59 | RestApiId: 60 | Ref: 'ApiGatewayRestApi' 61 | StatusCode: '401' 62 | AuthFailureGatewayResponse: 63 | Type: 'AWS::ApiGateway::GatewayResponse' 64 | Properties: 65 | ResponseParameters: 66 | gatewayresponse.header.Access-Control-Allow-Origin: "'*'" 67 | gatewayresponse.header.Access-Control-Allow-Headers: "'*'" 68 | ResponseType: UNAUTHORIZED 69 | RestApiId: 70 | Ref: 'ApiGatewayRestApi' 71 | StatusCode: '401' 72 | -------------------------------------------------------------------------------- /tutorials/api-gateway/serverless-lambda-auth/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const ProgressBarPlugin = require('progress-bar-webpack-plugin'); 3 | const slsw = require('serverless-webpack'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | const path = require('path'); 6 | 7 | const env = process.env.NODE_ENV === 'production' ? 'production' : 'development'; 8 | 9 | const config = { 10 | mode: env, 11 | entry: slsw.lib.entries, 12 | target: 'node', 13 | node: { 14 | __dirname: false, 15 | }, 16 | output: { 17 | libraryTarget: 'commonjs', 18 | path: path.join(__dirname, '.webpack'), 19 | filename: '[name].js', 20 | }, 21 | module: { 22 | rules: [{ 23 | test: /\.(js|jsx)$/, 24 | use: ['babel-loader'], 25 | }, 26 | ], 27 | }, 28 | plugins: [ 29 | new ProgressBarPlugin(), 30 | new webpack.DefinePlugin({ 31 | '.': '__dirname', 32 | }), 33 | new CopyWebpackPlugin({ 34 | patterns: [ 35 | { 36 | from: path.join(__dirname, '../resources'), 37 | to: '.', 38 | }, 39 | ], 40 | }), 41 | ], 42 | resolve: { 43 | modules: [ 44 | path.join(__dirname, '.'), 45 | 'node_modules', 46 | ], 47 | }, 48 | stats: { 49 | colors: true, 50 | }, 51 | devtool: false, 52 | }; 53 | 54 | module.exports = config; 55 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 17 | }, 18 | overrides: [ 19 | { 20 | files: [ 21 | '**/__tests__/*.{j,t}s?(x)', 22 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 23 | ], 24 | env: { 25 | jest: true 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/README.md: -------------------------------------------------------------------------------- 1 | # vue-ui 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Run your unit tests 19 | ``` 20 | yarn test:unit 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | yarn lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel' 3 | } 4 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-ui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "@dsb-norge/vue-keycloak-js": "^1.3.2", 13 | "axios": "^1.6.0", 14 | "vue": "^2.6.12" 15 | }, 16 | "devDependencies": { 17 | "@types/jest": "^26.0.20", 18 | "@typescript-eslint/eslint-plugin": "^4.23.0", 19 | "@typescript-eslint/parser": "^4.23.0", 20 | "@vue/cli-plugin-babel": "5.0.8", 21 | "@vue/cli-plugin-eslint": "5.0.8", 22 | "@vue/cli-plugin-typescript": "5.0.8", 23 | "@vue/cli-plugin-unit-jest": "5.0.8", 24 | "@vue/cli-service": "5.0.8", 25 | "@vue/eslint-config-typescript": "^7.0.0", 26 | "@vue/test-utils": "^1.2.0", 27 | "eslint": "^7.26.0", 28 | "eslint-plugin-vue": "^7.10.0", 29 | "glob-parent": "5.1.2", 30 | "jest": "^26.6.3", 31 | "postcss": "^8.4.31", 32 | "typescript": "~4.2.4", 33 | "vue-template-compiler": "^2.6.12", 34 | "ws": "^7.4.6", 35 | "yargs-parser": "13.1.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/keycloak-on-aws/80260d7e0e64f6378925424fbd5ec25e9775595f/tutorials/api-gateway/vue-ui/public/favicon.ico -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/public/silent-check-sso.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 97 | 98 | 177 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 45 | 46 | 47 | 63 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/src/kcConfig.ts: -------------------------------------------------------------------------------- 1 | export default KEYCLOAK_CONFIG; 2 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import axios from 'axios' 4 | import kcConfig from './kcConfig' 5 | import VueKeycloakJs from '@dsb-norge/vue-keycloak-js' 6 | 7 | Vue.config.productionTip = false 8 | 9 | function tokenInterceptor () { 10 | axios.interceptors.request.use(config => { 11 | config.headers.Authorization = `Bearer ${Vue.prototype.$keycloak.token}` 12 | return config 13 | }, error => { 14 | return Promise.reject(error) 15 | }) 16 | } 17 | 18 | Vue.use(VueKeycloakJs, { 19 | init: { 20 | // onLoad: 'login-required', 21 | onLoad: 'check-sso', 22 | silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', 23 | pkceMethod: 'S256', // https://www.keycloak.org/docs/latest/securing_apps/#methods 24 | }, 25 | config: { 26 | realm: kcConfig['realm'], 27 | url: kcConfig['auth-server-url'], 28 | clientId: kcConfig['resource'] 29 | }, 30 | onReady: () => { 31 | tokenInterceptor() 32 | /* eslint-disable no-new */ 33 | new Vue({ 34 | render: h => h(App) 35 | }).$mount('#app') 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | 6 | declare const KEYCLOAK_CONFIG: { 7 | [key: string]: unknown 8 | 'x-api-gw-url': string 9 | } 10 | 11 | // There is no need to add the following declare module once 12 | // https://github.com/dsb-norge/vue-keycloak-js/pull/64 is merged 13 | declare module "@dsb-norge/vue-keycloak-js" { 14 | import Vue, { PluginObject, PluginFunction } from "vue"; 15 | 16 | export class VueKeyCloakInstance { 17 | ready: boolean; // Flag indicating whether Keycloak has initialized and is ready 18 | authenticated: boolean; 19 | userName: string; // Username from Keycloak. Collected from tokenParsed['preferred_username'] 20 | fullName: string; // Full name from Keycloak. Collected from tokenParsed['name'] 21 | login: Function; // [Keycloak] login function 22 | loginFn: Function; // Alias for login 23 | logoutFn: Function; // Keycloak logout function 24 | createLoginUrl: Function; // Keycloak createLoginUrl function 25 | createLogoutUrl: Function; // Keycloak createLogoutUrl function 26 | createRegisterUrl: Function; // Keycloak createRegisterUrl function 27 | register: Function; // Keycloak register function 28 | accountManagement: Function; // Keycloak accountManagement function 29 | createAccountUrl: Function; // Keycloak createAccountUrl function 30 | loadUserProfile: Function; // Keycloak loadUserProfile function 31 | loadUserInfo: Function; // Keycloak loadUserInfo function 32 | subject: string; // The user id 33 | idToken: string; // The base64 encoded ID token. 34 | idTokenParsed: object; // The parsed id token as a JavaScript object. 35 | realmAccess: object; // The realm roles associated with the token. 36 | resourceAccess: object; // The resource roles associated with the token. 37 | refreshToken: string; // The base64 encoded refresh token that can be used to retrieve a new token. 38 | refreshTokenParsed: object; // The parsed refresh token as a JavaScript object. 39 | timeSkew: number; // The estimated time difference between the browser time and the Keycloak server in seconds. This value is just an estimation, but is accurate enough when determining if a token is expired or not. 40 | responseMode: string; // Response mode passed in init (default value is fragment). 41 | responseType: string; // Response type sent to Keycloak with login requests. This is determined based on the flow value used during initialization, but can be overridden by setting this value. 42 | hasRealmRole: Function; // Keycloak hasRealmRole function 43 | hasResourceRole: Function; // Keycloak hasResourceRole function 44 | token: string; // The base64 encoded token that can be sent in the Authorization header in requests to services 45 | tokenParsed: string; // The parsed token as a JavaScript object 46 | } 47 | 48 | export type VueKeyCloakOptions = { 49 | config?: { 50 | authUrl?: string; 51 | authRealm?: string; 52 | authClientId?: string; 53 | logoutRedirectUri?: string; 54 | }; 55 | init?: { 56 | onLoad?: string; 57 | pkceMethod?: 'S256'; 58 | }; 59 | onReady(keycloak: VueKeyCloakInstance): void; 60 | }; 61 | 62 | declare module "vue/types/vue" { 63 | // Declare augmentation for Vue 64 | interface Vue { 65 | $keycloak: VueKeyCloakInstance; 66 | } 67 | } 68 | 69 | export default class VueKeyCloak implements PluginObject { 70 | [key: string]: unknown; 71 | install: PluginFunction; 72 | static install( 73 | pVue: typeof Vue, 74 | options?: VueKeyCloakOptions | undefined 75 | ): void; 76 | 77 | constructor(options: VueKeyCloakOptions); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/tests/unit/example.spec.ts: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import HelloWorld from '@/components/HelloWorld.vue' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message' 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg } 9 | }) 10 | expect(wrapper.text()).toMatch(msg) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env", 16 | "jest" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /tutorials/api-gateway/vue-ui/vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | module.exports = { 5 | configureWebpack: { 6 | devtool: 'source-map', 7 | plugins: [ 8 | new webpack.DefinePlugin({ 9 | 'KEYCLOAK_CONFIG': JSON.stringify(require(path.join(__dirname, '../resources/keycloak.json'))) 10 | }), 11 | ] 12 | }, 13 | devServer: { 14 | host: 'localhost', // https://github.com/vuejs/vue-cli/issues/1616#issuecomment-536193059 15 | hot: true, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /tutorials/three-layer-subnets.template: -------------------------------------------------------------------------------- 1 | { 2 | "Resources": { 3 | "VPCB9E5F0B4": { 4 | "Type": "AWS::EC2::VPC", 5 | "Properties": { 6 | "CidrBlock": "10.0.0.0/16", 7 | "EnableDnsHostnames": true, 8 | "EnableDnsSupport": true, 9 | "InstanceTenancy": "default", 10 | "Tags": [ 11 | { 12 | "Key": "Name", 13 | "Value": "three-layer-subnets/VPC" 14 | } 15 | ] 16 | }, 17 | "Metadata": { 18 | "aws:cdk:path": "three-layer-subnets/VPC/Resource" 19 | } 20 | }, 21 | "VPCingressSubnet1SubnetBB7FDF67": { 22 | "Type": "AWS::EC2::Subnet", 23 | "Properties": { 24 | "CidrBlock": "10.0.0.0/24", 25 | "VpcId": { 26 | "Ref": "VPCB9E5F0B4" 27 | }, 28 | "AvailabilityZone": { 29 | "Fn::Select": [ 30 | 0, 31 | { 32 | "Fn::GetAZs": "" 33 | } 34 | ] 35 | }, 36 | "MapPublicIpOnLaunch": true, 37 | "Tags": [ 38 | { 39 | "Key": "aws-cdk:subnet-name", 40 | "Value": "ingress" 41 | }, 42 | { 43 | "Key": "aws-cdk:subnet-type", 44 | "Value": "Public" 45 | }, 46 | { 47 | "Key": "Name", 48 | "Value": "three-layer-subnets/VPC/ingressSubnet1" 49 | } 50 | ] 51 | }, 52 | "Metadata": { 53 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet1/Subnet" 54 | } 55 | }, 56 | "VPCingressSubnet1RouteTableEEF02A64": { 57 | "Type": "AWS::EC2::RouteTable", 58 | "Properties": { 59 | "VpcId": { 60 | "Ref": "VPCB9E5F0B4" 61 | }, 62 | "Tags": [ 63 | { 64 | "Key": "Name", 65 | "Value": "three-layer-subnets/VPC/ingressSubnet1" 66 | } 67 | ] 68 | }, 69 | "Metadata": { 70 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet1/RouteTable" 71 | } 72 | }, 73 | "VPCingressSubnet1RouteTableAssociation7700457B": { 74 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 75 | "Properties": { 76 | "RouteTableId": { 77 | "Ref": "VPCingressSubnet1RouteTableEEF02A64" 78 | }, 79 | "SubnetId": { 80 | "Ref": "VPCingressSubnet1SubnetBB7FDF67" 81 | } 82 | }, 83 | "Metadata": { 84 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet1/RouteTableAssociation" 85 | } 86 | }, 87 | "VPCingressSubnet1DefaultRouteC1C9D77C": { 88 | "Type": "AWS::EC2::Route", 89 | "Properties": { 90 | "RouteTableId": { 91 | "Ref": "VPCingressSubnet1RouteTableEEF02A64" 92 | }, 93 | "DestinationCidrBlock": "0.0.0.0/0", 94 | "GatewayId": { 95 | "Ref": "VPCIGWB7E252D3" 96 | } 97 | }, 98 | "DependsOn": [ 99 | "VPCVPCGW99B986DC" 100 | ], 101 | "Metadata": { 102 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet1/DefaultRoute" 103 | } 104 | }, 105 | "VPCingressSubnet1EIP46E19D36": { 106 | "Type": "AWS::EC2::EIP", 107 | "Properties": { 108 | "Domain": "vpc", 109 | "Tags": [ 110 | { 111 | "Key": "Name", 112 | "Value": "three-layer-subnets/VPC/ingressSubnet1" 113 | } 114 | ] 115 | }, 116 | "Metadata": { 117 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet1/EIP" 118 | } 119 | }, 120 | "VPCingressSubnet1NATGateway06A5D97E": { 121 | "Type": "AWS::EC2::NatGateway", 122 | "Properties": { 123 | "AllocationId": { 124 | "Fn::GetAtt": [ 125 | "VPCingressSubnet1EIP46E19D36", 126 | "AllocationId" 127 | ] 128 | }, 129 | "SubnetId": { 130 | "Ref": "VPCingressSubnet1SubnetBB7FDF67" 131 | }, 132 | "Tags": [ 133 | { 134 | "Key": "Name", 135 | "Value": "three-layer-subnets/VPC/ingressSubnet1" 136 | } 137 | ] 138 | }, 139 | "Metadata": { 140 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet1/NATGateway" 141 | } 142 | }, 143 | "VPCingressSubnet2SubnetE30F0091": { 144 | "Type": "AWS::EC2::Subnet", 145 | "Properties": { 146 | "CidrBlock": "10.0.1.0/24", 147 | "VpcId": { 148 | "Ref": "VPCB9E5F0B4" 149 | }, 150 | "AvailabilityZone": { 151 | "Fn::Select": [ 152 | 1, 153 | { 154 | "Fn::GetAZs": "" 155 | } 156 | ] 157 | }, 158 | "MapPublicIpOnLaunch": true, 159 | "Tags": [ 160 | { 161 | "Key": "aws-cdk:subnet-name", 162 | "Value": "ingress" 163 | }, 164 | { 165 | "Key": "aws-cdk:subnet-type", 166 | "Value": "Public" 167 | }, 168 | { 169 | "Key": "Name", 170 | "Value": "three-layer-subnets/VPC/ingressSubnet2" 171 | } 172 | ] 173 | }, 174 | "Metadata": { 175 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet2/Subnet" 176 | } 177 | }, 178 | "VPCingressSubnet2RouteTable8565F2D0": { 179 | "Type": "AWS::EC2::RouteTable", 180 | "Properties": { 181 | "VpcId": { 182 | "Ref": "VPCB9E5F0B4" 183 | }, 184 | "Tags": [ 185 | { 186 | "Key": "Name", 187 | "Value": "three-layer-subnets/VPC/ingressSubnet2" 188 | } 189 | ] 190 | }, 191 | "Metadata": { 192 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet2/RouteTable" 193 | } 194 | }, 195 | "VPCingressSubnet2RouteTableAssociation35C35494": { 196 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 197 | "Properties": { 198 | "RouteTableId": { 199 | "Ref": "VPCingressSubnet2RouteTable8565F2D0" 200 | }, 201 | "SubnetId": { 202 | "Ref": "VPCingressSubnet2SubnetE30F0091" 203 | } 204 | }, 205 | "Metadata": { 206 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet2/RouteTableAssociation" 207 | } 208 | }, 209 | "VPCingressSubnet2DefaultRoute8E2F45A7": { 210 | "Type": "AWS::EC2::Route", 211 | "Properties": { 212 | "RouteTableId": { 213 | "Ref": "VPCingressSubnet2RouteTable8565F2D0" 214 | }, 215 | "DestinationCidrBlock": "0.0.0.0/0", 216 | "GatewayId": { 217 | "Ref": "VPCIGWB7E252D3" 218 | } 219 | }, 220 | "DependsOn": [ 221 | "VPCVPCGW99B986DC" 222 | ], 223 | "Metadata": { 224 | "aws:cdk:path": "three-layer-subnets/VPC/ingressSubnet2/DefaultRoute" 225 | } 226 | }, 227 | "VPCapplicationSubnet1SubnetFE5BD7C4": { 228 | "Type": "AWS::EC2::Subnet", 229 | "Properties": { 230 | "CidrBlock": "10.0.2.0/24", 231 | "VpcId": { 232 | "Ref": "VPCB9E5F0B4" 233 | }, 234 | "AvailabilityZone": { 235 | "Fn::Select": [ 236 | 0, 237 | { 238 | "Fn::GetAZs": "" 239 | } 240 | ] 241 | }, 242 | "MapPublicIpOnLaunch": false, 243 | "Tags": [ 244 | { 245 | "Key": "aws-cdk:subnet-name", 246 | "Value": "application" 247 | }, 248 | { 249 | "Key": "aws-cdk:subnet-type", 250 | "Value": "Private" 251 | }, 252 | { 253 | "Key": "Name", 254 | "Value": "three-layer-subnets/VPC/applicationSubnet1" 255 | } 256 | ] 257 | }, 258 | "Metadata": { 259 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet1/Subnet" 260 | } 261 | }, 262 | "VPCapplicationSubnet1RouteTable06DA588A": { 263 | "Type": "AWS::EC2::RouteTable", 264 | "Properties": { 265 | "VpcId": { 266 | "Ref": "VPCB9E5F0B4" 267 | }, 268 | "Tags": [ 269 | { 270 | "Key": "Name", 271 | "Value": "three-layer-subnets/VPC/applicationSubnet1" 272 | } 273 | ] 274 | }, 275 | "Metadata": { 276 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet1/RouteTable" 277 | } 278 | }, 279 | "VPCapplicationSubnet1RouteTableAssociationBD8DBEDA": { 280 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 281 | "Properties": { 282 | "RouteTableId": { 283 | "Ref": "VPCapplicationSubnet1RouteTable06DA588A" 284 | }, 285 | "SubnetId": { 286 | "Ref": "VPCapplicationSubnet1SubnetFE5BD7C4" 287 | } 288 | }, 289 | "Metadata": { 290 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet1/RouteTableAssociation" 291 | } 292 | }, 293 | "VPCapplicationSubnet1DefaultRouteDDC3EE0F": { 294 | "Type": "AWS::EC2::Route", 295 | "Properties": { 296 | "RouteTableId": { 297 | "Ref": "VPCapplicationSubnet1RouteTable06DA588A" 298 | }, 299 | "DestinationCidrBlock": "0.0.0.0/0", 300 | "NatGatewayId": { 301 | "Ref": "VPCingressSubnet1NATGateway06A5D97E" 302 | } 303 | }, 304 | "Metadata": { 305 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet1/DefaultRoute" 306 | } 307 | }, 308 | "VPCapplicationSubnet2Subnet9AE2E808": { 309 | "Type": "AWS::EC2::Subnet", 310 | "Properties": { 311 | "CidrBlock": "10.0.3.0/24", 312 | "VpcId": { 313 | "Ref": "VPCB9E5F0B4" 314 | }, 315 | "AvailabilityZone": { 316 | "Fn::Select": [ 317 | 1, 318 | { 319 | "Fn::GetAZs": "" 320 | } 321 | ] 322 | }, 323 | "MapPublicIpOnLaunch": false, 324 | "Tags": [ 325 | { 326 | "Key": "aws-cdk:subnet-name", 327 | "Value": "application" 328 | }, 329 | { 330 | "Key": "aws-cdk:subnet-type", 331 | "Value": "Private" 332 | }, 333 | { 334 | "Key": "Name", 335 | "Value": "three-layer-subnets/VPC/applicationSubnet2" 336 | } 337 | ] 338 | }, 339 | "Metadata": { 340 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet2/Subnet" 341 | } 342 | }, 343 | "VPCapplicationSubnet2RouteTableBE360162": { 344 | "Type": "AWS::EC2::RouteTable", 345 | "Properties": { 346 | "VpcId": { 347 | "Ref": "VPCB9E5F0B4" 348 | }, 349 | "Tags": [ 350 | { 351 | "Key": "Name", 352 | "Value": "three-layer-subnets/VPC/applicationSubnet2" 353 | } 354 | ] 355 | }, 356 | "Metadata": { 357 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet2/RouteTable" 358 | } 359 | }, 360 | "VPCapplicationSubnet2RouteTableAssociationB08E8C3F": { 361 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 362 | "Properties": { 363 | "RouteTableId": { 364 | "Ref": "VPCapplicationSubnet2RouteTableBE360162" 365 | }, 366 | "SubnetId": { 367 | "Ref": "VPCapplicationSubnet2Subnet9AE2E808" 368 | } 369 | }, 370 | "Metadata": { 371 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet2/RouteTableAssociation" 372 | } 373 | }, 374 | "VPCapplicationSubnet2DefaultRouteBFF28947": { 375 | "Type": "AWS::EC2::Route", 376 | "Properties": { 377 | "RouteTableId": { 378 | "Ref": "VPCapplicationSubnet2RouteTableBE360162" 379 | }, 380 | "DestinationCidrBlock": "0.0.0.0/0", 381 | "NatGatewayId": { 382 | "Ref": "VPCingressSubnet1NATGateway06A5D97E" 383 | } 384 | }, 385 | "Metadata": { 386 | "aws:cdk:path": "three-layer-subnets/VPC/applicationSubnet2/DefaultRoute" 387 | } 388 | }, 389 | "VPCrdsSubnet1Subnet6ED1A3D4": { 390 | "Type": "AWS::EC2::Subnet", 391 | "Properties": { 392 | "CidrBlock": "10.0.4.0/28", 393 | "VpcId": { 394 | "Ref": "VPCB9E5F0B4" 395 | }, 396 | "AvailabilityZone": { 397 | "Fn::Select": [ 398 | 0, 399 | { 400 | "Fn::GetAZs": "" 401 | } 402 | ] 403 | }, 404 | "MapPublicIpOnLaunch": false, 405 | "Tags": [ 406 | { 407 | "Key": "aws-cdk:subnet-name", 408 | "Value": "rds" 409 | }, 410 | { 411 | "Key": "aws-cdk:subnet-type", 412 | "Value": "Isolated" 413 | }, 414 | { 415 | "Key": "Name", 416 | "Value": "three-layer-subnets/VPC/rdsSubnet1" 417 | } 418 | ] 419 | }, 420 | "Metadata": { 421 | "aws:cdk:path": "three-layer-subnets/VPC/rdsSubnet1/Subnet" 422 | } 423 | }, 424 | "VPCrdsSubnet1RouteTableAAE4BCE1": { 425 | "Type": "AWS::EC2::RouteTable", 426 | "Properties": { 427 | "VpcId": { 428 | "Ref": "VPCB9E5F0B4" 429 | }, 430 | "Tags": [ 431 | { 432 | "Key": "Name", 433 | "Value": "three-layer-subnets/VPC/rdsSubnet1" 434 | } 435 | ] 436 | }, 437 | "Metadata": { 438 | "aws:cdk:path": "three-layer-subnets/VPC/rdsSubnet1/RouteTable" 439 | } 440 | }, 441 | "VPCrdsSubnet1RouteTableAssociation2B358732": { 442 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 443 | "Properties": { 444 | "RouteTableId": { 445 | "Ref": "VPCrdsSubnet1RouteTableAAE4BCE1" 446 | }, 447 | "SubnetId": { 448 | "Ref": "VPCrdsSubnet1Subnet6ED1A3D4" 449 | } 450 | }, 451 | "Metadata": { 452 | "aws:cdk:path": "three-layer-subnets/VPC/rdsSubnet1/RouteTableAssociation" 453 | } 454 | }, 455 | "VPCrdsSubnet2Subnet7BC222EF": { 456 | "Type": "AWS::EC2::Subnet", 457 | "Properties": { 458 | "CidrBlock": "10.0.4.16/28", 459 | "VpcId": { 460 | "Ref": "VPCB9E5F0B4" 461 | }, 462 | "AvailabilityZone": { 463 | "Fn::Select": [ 464 | 1, 465 | { 466 | "Fn::GetAZs": "" 467 | } 468 | ] 469 | }, 470 | "MapPublicIpOnLaunch": false, 471 | "Tags": [ 472 | { 473 | "Key": "aws-cdk:subnet-name", 474 | "Value": "rds" 475 | }, 476 | { 477 | "Key": "aws-cdk:subnet-type", 478 | "Value": "Isolated" 479 | }, 480 | { 481 | "Key": "Name", 482 | "Value": "three-layer-subnets/VPC/rdsSubnet2" 483 | } 484 | ] 485 | }, 486 | "Metadata": { 487 | "aws:cdk:path": "three-layer-subnets/VPC/rdsSubnet2/Subnet" 488 | } 489 | }, 490 | "VPCrdsSubnet2RouteTable10277BE0": { 491 | "Type": "AWS::EC2::RouteTable", 492 | "Properties": { 493 | "VpcId": { 494 | "Ref": "VPCB9E5F0B4" 495 | }, 496 | "Tags": [ 497 | { 498 | "Key": "Name", 499 | "Value": "three-layer-subnets/VPC/rdsSubnet2" 500 | } 501 | ] 502 | }, 503 | "Metadata": { 504 | "aws:cdk:path": "three-layer-subnets/VPC/rdsSubnet2/RouteTable" 505 | } 506 | }, 507 | "VPCrdsSubnet2RouteTableAssociationBD2B5032": { 508 | "Type": "AWS::EC2::SubnetRouteTableAssociation", 509 | "Properties": { 510 | "RouteTableId": { 511 | "Ref": "VPCrdsSubnet2RouteTable10277BE0" 512 | }, 513 | "SubnetId": { 514 | "Ref": "VPCrdsSubnet2Subnet7BC222EF" 515 | } 516 | }, 517 | "Metadata": { 518 | "aws:cdk:path": "three-layer-subnets/VPC/rdsSubnet2/RouteTableAssociation" 519 | } 520 | }, 521 | "VPCIGWB7E252D3": { 522 | "Type": "AWS::EC2::InternetGateway", 523 | "Properties": { 524 | "Tags": [ 525 | { 526 | "Key": "Name", 527 | "Value": "three-layer-subnets/VPC" 528 | } 529 | ] 530 | }, 531 | "Metadata": { 532 | "aws:cdk:path": "three-layer-subnets/VPC/IGW" 533 | } 534 | }, 535 | "VPCVPCGW99B986DC": { 536 | "Type": "AWS::EC2::VPCGatewayAttachment", 537 | "Properties": { 538 | "VpcId": { 539 | "Ref": "VPCB9E5F0B4" 540 | }, 541 | "InternetGatewayId": { 542 | "Ref": "VPCIGWB7E252D3" 543 | } 544 | }, 545 | "Metadata": { 546 | "aws:cdk:path": "three-layer-subnets/VPC/VPCGW" 547 | } 548 | }, 549 | "CDKMetadata": { 550 | "Type": "AWS::CDK::Metadata", 551 | "Properties": { 552 | "Modules": "aws-cdk=1.84.0,@aws-cdk/assets=1.84.0,@aws-cdk/aws-cloudwatch=1.84.0,@aws-cdk/aws-ec2=1.84.0,@aws-cdk/aws-events=1.84.0,@aws-cdk/aws-iam=1.84.0,@aws-cdk/aws-kms=1.84.0,@aws-cdk/aws-logs=1.84.0,@aws-cdk/aws-s3=1.84.0,@aws-cdk/aws-s3-assets=1.84.0,@aws-cdk/aws-ssm=1.84.0,@aws-cdk/cloud-assembly-schema=1.84.0,@aws-cdk/core=1.84.0,@aws-cdk/cx-api=1.84.0,@aws-cdk/region-info=1.84.0,jsii-runtime=node.js/v14.4.0" 553 | }, 554 | "Metadata": { 555 | "aws:cdk:path": "three-layer-subnets/CDKMetadata/Default" 556 | } 557 | } 558 | } 559 | } -------------------------------------------------------------------------------- /vetur.config.js: -------------------------------------------------------------------------------- 1 | // vetur.config.js 2 | /** @type {import('vls').VeturConfig} */ 3 | module.exports = { 4 | // **optional** default: `{}` 5 | // override vscode settings 6 | // Notice: It only affects the settings used by Vetur. 7 | settings: { 8 | }, 9 | // **optional** default: `[{ root: './' }]` 10 | // support monorepos 11 | projects: [ 12 | './examples/api-gw/vue-ui', // shorthand for only root. 13 | ] 14 | } 15 | --------------------------------------------------------------------------------