├── .github └── workflows │ ├── ci.yml │ ├── test.yml │ └── wiki.yml ├── .gitignore ├── Contributing.md ├── Licence ├── README.md ├── ansible ├── ansible.cfg ├── boundary.yml ├── cleanup.yml ├── handlers │ ├── container_healthcheck.yml │ ├── fail_task.yml │ └── service_healthcheck.yml ├── host_vars │ ├── hashicorp │ └── localhost ├── inventory │ └── inventory.ini ├── playbook.yml ├── roles │ ├── boundary │ │ ├── defaults │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── boundary_iac.yml │ │ │ ├── deploy.yml │ │ │ ├── init_credentials.yml │ │ │ └── main.yml │ │ └── vars │ │ │ └── main.yml │ ├── cleanup │ │ ├── tasks │ │ │ ├── main.yml │ │ │ ├── secrets_file.yml │ │ │ └── tokens.yml │ │ └── vars │ │ │ └── main.yml │ ├── prepare_env │ │ ├── tasks │ │ │ ├── docker_images.yml │ │ │ ├── files_and_directories.yml │ │ │ └── main.yml │ │ └── vars │ │ │ └── main.yml │ ├── provision │ │ ├── default │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── vars │ │ │ └── main.yml │ ├── terraform │ │ ├── defaults │ │ │ └── main.yml │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── cred_store_ssh.yml │ │ │ ├── main.yml │ │ │ ├── tfvars_token.yml │ │ │ └── vault_iac.yml │ │ └── vars │ │ │ └── main.yml │ └── vault │ │ ├── defaults │ │ └── main.yml │ │ ├── tasks │ │ ├── deploy.yml │ │ ├── docker_pull_status.yml │ │ ├── main.yml │ │ └── secrets.yml │ │ └── vars │ │ └── main.yml ├── terraform.yml └── utils │ └── secrets.yml ├── artifacts ├── diagrams │ ├── boundary.py │ └── vault.py ├── wiki.md └── wiki │ ├── app.js │ ├── index.html │ └── style.css ├── boundary ├── config │ └── boundary.hcl └── terraform │ ├── README.md │ ├── main.tf │ ├── output.tf │ ├── provider.tf │ ├── terraform.tf │ ├── terraform.tfvars.sample │ └── variables.tf ├── deploy ├── boundary.yml └── vault.yml ├── g ├── provision └── specs.txt ├── requirements.txt ├── requirements.yml ├── scripts ├── cleanup.sh ├── deploy.sh ├── init.sh ├── install_services.sh ├── install_vagrant.sh ├── install_virtual_box.sh ├── linter.sh ├── pull-images.sh └── tls-gen.sh ├── start.sh ├── vagrantfile └── vault ├── config └── vault.hcl ├── policy └── kv-policy.hcl └── terraform ├── auth.tf ├── main.tf ├── output.tf ├── policies.tf ├── policies ├── admin-policy.hcl ├── boundary-controller.hcl ├── boundary-transit.hcl ├── ssh.hcl ├── testapp.hcl └── transit-admin.hcl ├── secrets.tf ├── terraform.tfvars.sample └── variables.tf /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: BVSTACK CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | jobs: 14 | ## build to be implemented 15 | 16 | release: 17 | if: github.event_name != 'pull_request' 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Bump version and push tag 21 | id: bump 22 | uses: mathieudutour/github-tag-action@a22cf08638b34d5badda920f9daf6e72c477b07b 23 | with: 24 | github_token: ${{ secrets.GITHUB_TOKEN }} 25 | default_bump: patch 26 | 27 | - name: Build Changelog 28 | id: github_release 29 | uses: mikepenz/release-changelog-builder-action@f3fc77b47b74e78971fffecb2102ae6eac9a44d6 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | fromTag: ${{ steps.bump.outputs.previous_tag }} 33 | toTag: ${{ steps.bump.outputs.new_tag }} 34 | 35 | - name: Create Release 36 | uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 37 | with: 38 | body: ${{ steps.bump.outputs.changelog }} 39 | tag_name: ${{ steps.bump.outputs.new_tag }} -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: BVSTACK Test Pipeline 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - 'main' 7 | pull_request: 8 | 9 | env: 10 | VAULT_ADDR: ${{ vars.VAULT_ADDR }} 11 | VAULT_TOKEN: ${{ vars.VAULT_TOKEN }} 12 | BOUNDARY_ADDR: ${{ vars.BOUNDARY_ADDR }} 13 | 14 | jobs: 15 | lint-boundary-terraform: 16 | runs-on: ubuntu-latest 17 | defaults: 18 | run: 19 | working-directory: "boundary/terraform/" 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v4 23 | 24 | - name: setup terraform cli 25 | uses: hashicorp/setup-terraform@v3 26 | 27 | - name: Terraform fmt 28 | id: fmt 29 | run: terraform fmt -check 30 | continue-on-error: true 31 | 32 | - name: Terraform Init 33 | id: init 34 | run: terraform init 35 | 36 | - name: Terraform Validate 37 | id: validate 38 | run: terraform validate -no-color 39 | 40 | - name: validate stdout 41 | run: echo "${{ steps.validate.outputs.stdout }}" 42 | 43 | - name: validate sterr 44 | run: echo "${{ steps.validate.outputs.stderr }}" 45 | 46 | - name: validate exitcode 47 | run: echo "${{ steps.validate.outputs.exitcode }}" 48 | 49 | lint-vault-terraform: 50 | runs-on: ubuntu-latest 51 | defaults: 52 | run: 53 | working-directory: "vault/terraform/" 54 | steps: 55 | - name: Checkout code 56 | uses: actions/checkout@v4 57 | 58 | - name: setup terraform cli 59 | uses: hashicorp/setup-terraform@v3 60 | 61 | - name: Terraform fmt 62 | id: fmt 63 | run: terraform fmt -check 64 | continue-on-error: true 65 | 66 | - name: Terraform Init 67 | id: init 68 | run: terraform init 69 | 70 | - name: Terraform Validate 71 | id: validate 72 | run: terraform validate -no-color 73 | 74 | - run: echo ${{ steps.plan.outputs.stdout }} 75 | - run: echo ${{ steps.plan.outputs.stderr }} 76 | - run: echo ${{ steps.plan.outputs.exitcode }} 77 | 78 | lint-ansible: 79 | runs-on: ubuntu-latest 80 | defaults: 81 | run: 82 | working-directory: ./scripts 83 | shell: bash 84 | steps: 85 | - name: Checkout code 86 | uses: actions/checkout@v4 87 | 88 | - name: setup python 89 | uses: actions/setup-python@v5 90 | with: 91 | python-version: '3.10' 92 | cache: 'pip' 93 | 94 | - name: install ansible 95 | run: | 96 | pip install -U pip 97 | pip install ansible wheel 98 | 99 | - name: check playbook and role's syntax 100 | run: bash linter.sh ansible -------------------------------------------------------------------------------- /.github/workflows/wiki.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy Wiki to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: 8 | - main 9 | paths: 10 | - 'artifacts/**' 11 | 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 17 | permissions: 18 | contents: read 19 | pages: write 20 | id-token: write 21 | 22 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 23 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 24 | concurrency: 25 | group: "pages" 26 | cancel-in-progress: false 27 | 28 | jobs: 29 | generate_diagrams: 30 | runs-on: ubuntu-latest 31 | defaults: 32 | run: 33 | working-directory: "artifacts/diagrams/" 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 37 | 38 | - name: setup python 39 | uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 #v5 40 | with: 41 | python-version: '3.10' 42 | cache: 'pip' 43 | 44 | - name: install diagram as code library 45 | run: | 46 | pip install -U pip 47 | pip install diagrams 48 | sudo apt update 49 | sudo apt install graphviz 50 | 51 | - name: generate diagrams 52 | run: | 53 | python vault.py 54 | python boundary.py 55 | mv *.png ${{github.workspace}}/artifacts/wiki/ 56 | ls ../wiki/ 57 | 58 | - uses: actions/upload-artifact@v4 59 | with: 60 | name: diagrams 61 | path: ${{github.workspace}}/artifacts/wiki/ 62 | 63 | deploy-wiki: 64 | needs: generate_diagrams 65 | environment: 66 | name: github-pages 67 | url: ${{ steps.deployment.outputs.page_url }} 68 | runs-on: ubuntu-latest 69 | steps: 70 | - name: Checkout 71 | uses: actions/checkout@v4 72 | 73 | - name : download diagram artifacts 74 | uses: actions/download-artifact@v4 75 | with: 76 | name: diagrams 77 | path: ${{github.workspace}}/artifacts/wiki 78 | merge-multiple: true 79 | 80 | - name: Setup Pages 81 | uses: actions/configure-pages@v5 82 | - name: Upload artifact 83 | uses: actions/upload-pages-artifact@v3 84 | with: 85 | # Upload entire repository 86 | path: './artifacts/wiki' 87 | - name: Deploy to GitHub Pages 88 | id: deployment 89 | uses: actions/deploy-pages@v4 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/**audit** 2 | 3 | # Local .terraform directories 4 | **/.terraform/* 5 | 6 | # .tfstate files 7 | *.tfstate 8 | *.tfstate.* 9 | *.terraform.lock.hcl* 10 | 11 | # Crash log files 12 | crash.log 13 | crash.*.log 14 | 15 | #### Exclude all .tfvars files, which are likely to contain sensitive data, such as 16 | #### password, private keys, and other secrets. These should not be part of version 17 | #### control as they are data points which are potentially sensitive and subject 18 | #### to change depending on the environment. 19 | *.tfvars 20 | *.tfvars.json 21 | 22 | # Ignore override files as they are usually used to override resources locally and so 23 | # are not checked in 24 | override.tf 25 | override.tf.json 26 | *_override.tf 27 | *_override.tf.json 28 | 29 | # Include override files you do wish to add to version control using negated pattern 30 | # !example_override.tf 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | 35 | # Ignore CLI configuration files 36 | .terraformrc 37 | terraform.rc 38 | 39 | architecture/ 40 | 41 | 42 | **/venv 43 | 44 | *secret*.txt 45 | **log**.txt 46 | 47 | to-do 48 | to-research 49 | **local.yml 50 | .vagrant 51 | *VBox*.log 52 | **/bin/boundary** 53 | **test-play** 54 | virtualenv 55 | 56 | **boundary*creds*.txt -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines for HashiCorp Boundary and Vault Stack 2 | 3 | Thank you for considering contributing to the HashiCorp Boundary and Vault Stack project! Your contributions are invaluable to the project's improvement. To ensure smooth collaboration, please follow the guidelines outlined below. 4 | 5 | ## Table of Contents 6 | - [Getting Started](#getting-started) 7 | - [Types of Contributions](#types-of-contributions) 8 | - [Reporting Issues](#reporting-issues) 9 | - [Commit Messages](#commit-messages) 10 | - [Submitting Pull Requests](#submitting-pull-requests) 11 | - [Code Style and Best Practices](#code-style-and-best-practices) 12 | - [Guidelines for Specific Tasks](#guidelines-for-specific-tasks) 13 | - [Terraform](#terraform) 14 | - [Ansible](#ansible) 15 | - [Shell Scripting](#shell-scripting) 16 | - [CI/CD](#cicd) 17 | - [Communication](#communication) 18 | - [License](#license) 19 | 20 | ## Getting Started 21 | 22 | 1. **Fork the Repository**: Begin by forking the repository to your GitHub account. 23 | 24 | 2. **Clone the Repository**: Clone your forked repository to your local machine: 25 | ```bash 26 | git clone https://github.com/your-username/boundary-vault-stack.git 27 | cd boundary-vault-stack 28 | ``` 29 | 30 | 3. **Set Up Your Environment**: Ensure you have the necessary dependencies installed as outlined in the [documentation](https://devopshobbies.github.io/boundary-vault-stack/). 31 | 32 | 4. **Review the Documentation**: Familiarize yourself with the project by thoroughly reading the [documentation](https://devopshobbies.github.io/boundary-vault-stack/) and reviewing the [automation workflow diagram](https://linktw.in/PloXtt). 33 | 34 | ## Types of Contributions 35 | 36 | ### Reporting Issues 37 | 38 | If you encounter any bugs, errors, or have suggestions for improvements: 39 | 40 | - **Search Existing Issues**: Before submitting a new issue, check if it has already been reported. 41 | - **Create a New Issue**: If it’s a new issue, provide detailed information such as steps to reproduce, expected vs. actual results, and any relevant screenshots or logs. 42 | - **Link to Related Tasks**: If your issue relates to any of the [TODOs](https://github.com/devopshobbies/boundary-vault-stack/tree/main/#to-do), reference the corresponding task. 43 | 44 | ### Commit Messages 45 | 46 | **Use Conventional Commits**: The project follows [semantic versioning](https://semver.org/) to ensure proper releases. Start your commits with a prefix such as `fix:`, `feat:`, `chore:`, or `doc:`. 47 | 48 | - **Imperative Mood**: Write commit messages as commands (e.g., "Add Vagrantfile for VM provisioning"). 49 | - **Be Concise but Descriptive**: Provide enough detail to understand the change. 50 | - **Commit Message Conventions**: Use `doc:` for any changes related to documentation. 51 | 52 | ### Submitting Pull Requests 53 | 54 | When submitting pull requests (PRs): 55 | 56 | 1. **Create a Branch**: Create a new branch for your feature or bug fix. Avoid working directly on the `main` branch. 57 | ```bash 58 | git checkout -b feature/your-feature-name 59 | ``` 60 | 61 | 2. **Make Atomic Commits**: Ensure each commit is focused and addresses a single change, following the Commit Messages section. 62 | 63 | 3. **Follow Best Practices**: Ensure your code adheres to the project's best practices. 64 | 65 | 4. **Test Your Changes**: Run tests and ensure your changes do not break existing code. 66 | 67 | 5. **Update Documentation**: If your change requires documentation updates, include them in your PR. Use the `documentation` label and provide additional context in the PR description. 68 | 69 | 6. **Submit the PR**: Push your branch to GitHub and open a pull request against the `main` branch. Link the PR to the corresponding issue(s). 70 | 71 | ### Code Style and Best Practices 72 | 73 | - **Use Meaningful Names**: Choose descriptive names for variables and tasks. 74 | - **Keep Tasks Small and Focused**: Each function should perform a single task. 75 | - **DRY (Don’t Repeat Yourself)**: Reuse code wherever possible. 76 | - **Comment Your Code**: Add comments where the code is not self-explanatory. 77 | - **Adhere to Best Practices**: Refer to the [Guidelines for Specific Tasks](#guidelines-for-specific-tasks) for Terraform, Ansible, and Shell Scripting. 78 | 79 | ## Guidelines for Specific Tasks 80 | 81 | ### Terraform 82 | 83 | - **State Management**: Configure remote state management properly, especially when working with remote backends. 84 | - **Output Values**: Make output values informative and useful. 85 | - **Avoid Unnecessary Loops**: Minimize the use of `foreach` and loops for variables, and avoid hardcoding values. 86 | 87 | ### Ansible 88 | 89 | - **Role Organization**: Keep roles modular and reusable. 90 | - **Handlers and Utilities**: Use [handlers](./ansible/handlers/) and [utilities](./ansible/utils/) for frequently repeated tasks. 91 | 92 | ### Shell Scripting 93 | 94 | - **Logging**: Implement consistent logging across all shell scripts. Use a custom logger function as outlined in the TODOs. 95 | - **Error Handling**: Ensure scripts handle errors gracefully and provide informative messages. 96 | 97 | ### CI/CD 98 | 99 | - **GitHub Actions**: Contribute to the existing CI/CD pipeline by implementing automated testing, linting, and security scans for pull requests. 100 | 101 | ## Communication 102 | 103 | - **Stay Updated**: Regularly check for project updates and communicate with maintainers about significant contributions. 104 | - **Respectful Collaboration**: Follow the code of conduct to maintain a respectful and inclusive environment. 105 | 106 | ## License 107 | 108 | By contributing to this project, you agree that your contributions will be licensed under the project's [license](./LICENSE). 109 | 110 | --- 111 | 112 | Thank you for your interest in contributing to the HashiCorp Boundary and Vault Stack! We look forward to your contributions. -------------------------------------------------------------------------------- /Licence: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Hynek Schlawack and the attrs contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HashiCorp Boundary and Vault Stack 2 | 3 | Deploy a Self-Hosted HCP Vault and Boundary stack using end-to-end automation. 4 | 5 | ## What This Project Offers 6 | 7 | This project provides a comprehensive, hands-on experience in Infrastructure as Code (IaC) and Configuration Management. It simulates a real-world infrastructure environment with a focus on end-to-end automation, enabling DevOps engineers to collaboratively deliver a reliable, production-ready stack. Key deliverables include detailed documentation and diagrams. 8 | 9 | > As of [the latest release](https://github.com/devopshobbies/boundary-vault-stack/releases/latest), BVSTACK covers **steps 0-3** of the [DevOpsHobbies Ultimate Roadmap](https://github.com/devopshobbies/devops-roadmap). 10 | 11 | ## 💻 Toolchain 12 | ![Vault](https://img.shields.io/badge/vault-%231A1918.svg?style=for-the-badge&logo=vault) 13 | ![LINUX](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black) 14 | ![Ansible](https://img.shields.io/badge/ansible-%231A1918.svg?style=for-the-badge&logo=ansible&logoColor=white) 15 | ![Terraform](https://img.shields.io/badge/terraform-%235835CC.svg?style=for-the-badge&logo=terraform&logoColor=white) 16 | ![Boundary](https://img.shields.io/badge/Boundary-%231A1918.svg?style=for-the-badge&logo=hashicorp&logoColor=red) 17 | ![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white) 18 | ![Vagrant](https://img.shields.io/badge/vagrant-%231A1918.svg?style=for-the-badge&logo=vagrant&logoColor=blue) 19 | ![Postgres](https://img.shields.io/badge/postgres-%23316192.svg?style=for-the-badge&logo=postgresql&logoColor=white) 20 | ![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54) 21 | [![Bash](https://img.shields.io/badge/Bash-1f425f.svg?style=for-the-badge&logo=image%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw%2FeHBhY2tldCBiZWdpbj0i77u%2FIiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8%2BIDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE3MDg2QTAyQUZCMzExRTVBMkQxRDMzMkJDMUQ4RDk3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE3MDg2QTAzQUZCMzExRTVBMkQxRDMzMkJDMUQ4RDk3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTcwODZBMDBBRkIzMTFFNUEyRDFEMzMyQkMxRDhEOTciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTcwODZBMDFBRkIzMTFFNUEyRDFEMzMyQkMxRDhEOTciLz4gPC9yZGY6RGVzY3JpcHRpb24%2BIDwvcmRmOlJERj4gPC94OnhtcG1ldGE%2BIDw%2FeHBhY2tldCBlbmQ9InIiPz6lm45hAAADkklEQVR42qyVa0yTVxzGn7d9Wy03MS2ii8s%2BeokYNQSVhCzOjXZOFNF4jx%2BMRmPUMEUEqVG36jo2thizLSQSMd4N8ZoQ8RKjJtooaCpK6ZoCtRXKpRempbTv5ey83bhkAUphz8fznvP8znn%2B%2F3NeEEJgNBoRRSmz0ub%2FfuxEacBg%2FDmYtiCjgo5NG2mBXq%2BH5I1ogMRk9Zbd%2BQU2e1ML6VPLOyf5tvBQ8yT1lG10imxsABm7SLs898GTpyYynEzP60hO3trHDKvMigUwdeaceacqzp7nOI4n0SSIIjl36ao4Z356OV07fSQAk6xJ3XGg%2BLCr1d1OYlVHp4eUHPnerU79ZA%2F1kuv1JQMAg%2BE4O2P23EumF3VkvHprsZKMzKwbRUXFEyTvSIEmTVbrysp%2BWr8wfQHGK6WChVa3bKUmdWou%2BjpArdGkzZ41c1zG%2Fu5uGH4swzd561F%2BuhIT4%2BLnSuPsv9%2BJKIpjNr9dXYOyk7%2FBZrcjIT4eCnoKgedJP4BEqhG77E3NKP31FO7cfQA5K0dSYuLgz2TwCWJSOBzG6crzKK%2BohNfni%2Bx6OMUMMNe%2Fgf7ocbw0v0acKg6J8Ql0q%2BT%2FAXR5PNi5dz9c71upuQqCKFAD%2BYhrZLEAmpodaHO3Qy6TI3NhBpbrshGtOWKOSMYwYGQM8nJzoFJNxP2HjyIQho4PewK6hBktoDcUwtIln4PjOWzflQ%2Be5yl0yCCYgYikTclGlxadio%2BBQCSiW1UXoVGrKYwH4RgMrjU1HAB4vR6LzWYfFUCKxfS8Ftk5qxHoCUQAUkRJaSEokkV6Y%2F%2BJUOC4hn6A39NVXVBYeNP8piH6HeA4fPbpdBQV5KOx0QaL1YppX3Jgk0TwH2Vg6S3u%2BdB91%2B%2FpuNYPYFl5uP5V7ZqvsrX7jxqMXR6ff3gCQSTzFI0a1TX3wIs8ul%2Bq4HuWAAiM39vhOuR1O1fQ2gT%2F26Z8Z5vrl2OHi9OXZn995nLV9aFfS6UC9JeJPfuK0NBohWpCHMSAAsFe74WWP%2BvT25wtP9Bpob6uGqqyDnOtaeumjRu%2ByFu36VntK%2FPA5umTJeUtPWZSU9BCgud661odVp3DZtkc7AnYR33RRC708PrVi1larW7XwZIjLnd7R6SgSqWSNjU1B3F72pz5TZbXmX5vV81Yb7Lg7XT%2FUXriu8XLVqw6c6XqWnBKiiYU%2BMt3wWF7u7i91XlSEITwSAZ%2FCzAAHsJVbwXYFFEAAAAASUVORK5CYII%3D)](https://www.gnu.org/software/bash/) 22 | 23 | ## Pre-requisites 24 | - [Vagrant](https://developer.hashicorp.com/vagrant/downloads) 25 | - [Virtualbox](https://virtualbox.org/wiki/Linux_Downloads) 26 | - Python => 3.10.12 27 | - Pip 28 | - venv 29 | 30 | ## How to Use 31 | 1. **Read the Documentation**: Before getting started, ensure you have thoroughly reviewed the [project documentation](https://devopshobbies.github.io/boundary-vault-stack/), the [automation workflow diagram](https://linktw.in/nWgoiO) and installed the **prerequisites**. 32 | 33 | 2. **Configure Variables**: Create your own `tfvars` file based on the samples provided in the [Boundary](./boundary/terraform/terraform.tfvars.sample) and [Vault](./vault/terraform/terraform.tfvars.sample) directories. Alternatively, you can remove the `.sample` extension from the provided sample files to use the default values. 34 | 35 | 3. **Run the Start Script**: Begin the setup by running the `start.sh` script in your desired environment: 36 | ```bash 37 | # Run in development: 38 | ./start.sh -e development 39 | ``` 40 | > you'll be prompted to choose which NIC you want to bridge to by Vagrant. 41 | 42 | 4. **Enter Vault Password**: You will be prompted to enter the Vault password four times to decrypt Ansible Vault-encrypted files (e.g., `inventory.ini`) unless the related [issue](https://github.com/devopshobbies/boundary-vault-stack/issues/24) is resolved. 43 | 44 | >**Note**: The default `ansible-vault-pass` is `BVSTACK`. This is provided for simplicity in the sample; ensure you use a strong password for your Ansible Vault-encrypted files. 45 | 46 | > **Note** 47 | > The stack assumes that your host machine acts as the Ansible/Terraform controller. If you have the resources, it's recommended to spin up a separate VM to serve as the controller by cloning and running the project on that VM. after that you can export STACK_SERVER environment variable and set it to false this enables you to keep your host machine clean and isolated. Otherwise, don't even bother you won't be losing much. [learn more about STACK_SERVER](https://devopshobbies.github.io/boundary-vault-stack/#environment-variables) 48 | 49 | For further assistance on exit/return codes and configurations, refer to the [documentation](https://devopshobbies.github.io/boundary-vault-stack/). 50 | 51 | ## To-Do List 52 | 53 | ### Terraform 54 | 55 | - [ ] Add a **Vagrantfile** to provision a VM using the **Vagrant** provider of your choice, based on the [specifications](./provision/specs.txt). 56 | - [ ] Provision an **EC2** instance using the **AWS** provider based on the [specifications](./provision/specs.txt) and additional required configurations. 57 | - [ ] Provision an **Azure** VM using the **Azure** provider based on the [specifications](./provision/specs.txt) and additional required configurations. 58 | - [ ] Provision a VM on an ESXi server using the **vSphere** provider based on the [specifications](./provision/specs.txt). 59 | - [ ] Add a remote backend option for Boundary and Vault. 60 | - [ ] Implement additional Vault authentication methods. 61 | - [ ] Enhance Terraform output values for both Boundary and Vault. 62 | - [ ] Implement Policy as Code (PaC) to validate Terraform policies. 63 | 64 | ### Packer 65 | 66 | - [ ] Add a Packer custom image template for VMware vSphere using the [specifications](./provision/specs.txt). 67 | 68 | ### Ansible 69 | 70 | - [ ] Install and configure Terraform on the **control node** using the `prepare_env` role. 71 | - [ ] Install and configure Docker on **target (managed) nodes** using the `prepare_env` role. 72 | - [ ] Template `tfvars` files to handle specific variables for both Boundary and Vault Terraform providers. 73 | - [ ] Create a well-organized Ansible template for Vault and Boundary configurations. 74 | - [ ] Update environment variable declarations in Ansible roles to use the `environment` attribute instead of inline definitions in the `shell` module. 75 | - [ ] Add proper configurations to serve the stack as a reverse proxy in the `serve` directory (tool optional). 76 | - [ ] Update `boundary.yml` to use environment variables instead of hardcoding, then manage the export of these variables with Ansible. 77 | - [ ] Convert Docker Compose files to corresponding Ansible modules using the `community.docker.docker_container` collection as an optional deployment method. 78 | - [ ] Implement Ansible Molecule scenarios to test different aspects of your roles. 79 | - [ ] Choose which provider to provision based on a user-defined or environment variable when handling provisions with Ansible. 80 | 81 | ### CI/CD 82 | 83 | - [ ] Implement automated testing using GitHub Actions for pull requests. 84 | 85 | ### Shell Scripting 86 | 87 | - [ ] Write a custom logger function and implement it throughout all shell scripts for better error handling and logging (in the `log` directory). 88 | - [ ] Use `case` statements instead of `if` for argument handling in `init.sh`. 89 | - [ ] Update `start.sh` to prompt for the Ansible Vault password once and use it for all operations. 90 | - [ ] Replace sleep commands in `start.sh` with the appropriate Ansible `wait_for` modules. 91 | - [ ] Remove the Vault root token in the `cleanup` script. 92 | 93 | ## Contribution 94 | 95 | All contributions are welcome! Please read the [Contributing Guidelines](./CONTRIBUTING.md) for more information. 96 | 97 | ## Credit and Maintenance 98 | 99 | **Copyright © 2024 [Shayan Ghani](https://github.com/Shayan-Ghani) - shayan.ghani.tech@gmail.com** 100 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking=false -------------------------------------------------------------------------------- /ansible/boundary.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: deploy and apply boundary tf modules 3 | hosts: hashicorp 4 | roles: 5 | - boundary 6 | -------------------------------------------------------------------------------- /ansible/cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: clean up stack secrets and files 4 | hosts: hashicorp 5 | roles: 6 | - cleanup 7 | -------------------------------------------------------------------------------- /ansible/handlers/container_healthcheck.yml: -------------------------------------------------------------------------------- 1 | - name: wait for the containers to get ready 2 | ansible.builtin.wait_for: 3 | timeout: 8 4 | delegate_to: localhost 5 | 6 | - name: check if contianer is healthy 7 | debug: 8 | msg: "Container {{ item.container.Name }} is healthy" 9 | with_items : 10 | - "{{ INFO.results }}" 11 | loop_control: 12 | label: "{{ item.container.Name | default('Unknown Container', true ) }}" 13 | when: 14 | - item is defined 15 | - item.container.State.Running | bool 16 | - item.container.State.Restarting != true 17 | 18 | - name: handle errors if occurred 19 | include_tasks: "{{handlers}}/fail_task.yml" 20 | vars: 21 | message: "Container {{ item.container.Name }} is NOT healthy" 22 | with_items : 23 | - "{{ INFO.results }}" 24 | loop_control: 25 | label: "ID : {{ item.container.Id | default('Unknown Container ID', true ) }} , NAME: {{ item.container.Name | default('Unknown Container ID', true ) }}" 26 | when : 27 | - item is not defined or item.container.State.Running != true 28 | 29 | - name: handle errors if container not healthy 30 | include_tasks: "{{handlers}}/service_healthcheck.yml" 31 | vars: 32 | NAMES: "{{ services }}" 33 | PATH: "{{ service_path }}" 34 | with_items : 35 | - "{{ INFO.results }}" 36 | when: 37 | - item is defined 38 | - item.exists != true 39 | - item.container.Name != '/vault' 40 | 41 | - name: fetch the logfile for the unhealthy container(s) 42 | # become: true 43 | fetch: 44 | src: "{{ item.container.LogPath }}" 45 | dest: "{{ log_dir }}/docker/{{item.container.Name}}.txt" 46 | flat: true 47 | fail_on_missing: true 48 | with_items : 49 | - "{{ INFO.results }}" 50 | loop_control: 51 | label: "{{ item.container.LogPath | default('Unknown Container', true ) }}" 52 | when: item.container.State.Running != true 53 | 54 | -------------------------------------------------------------------------------- /ansible/handlers/fail_task.yml: -------------------------------------------------------------------------------- 1 | - name: Fail if a task failed 2 | fail: 3 | msg: "{{ output.stderr | default(output.stdout, true) | default(message, true) }}" 4 | when: output.rc != 0 -------------------------------------------------------------------------------- /ansible/handlers/service_healthcheck.yml: -------------------------------------------------------------------------------- 1 | - name: fetch the logs for the service(s) 2 | shell: docker compose -f {{ PATH }} logs {{ item }} --no-color 3 | with_items : 4 | - "{{ NAMES }}" 5 | loop_control: 6 | label: "{{ item | default('Unknown Service', true ) }}" 7 | register: service_log 8 | ignore_errors: true 9 | 10 | - name: Save logs to a file 11 | copy: 12 | content: "{{ service_log.stdout | default(service_log.stderr, true) }}" 13 | dest: "{{ log_dir }}/docker/{{ item }}.txt" 14 | with_items: 15 | - "{{ NAMES }}" 16 | loop_control: 17 | label: "{{ item | default('Unknown Service', true ) }}" 18 | 19 | - name: handle errors if occurred 20 | include_tasks: "{{handlers}}/fail_task.yml" 21 | vars: 22 | message: "Service {{ item }} is NOT healthy" 23 | with_items : 24 | - "{{ NAMES }}" 25 | loop_control: 26 | label: "{{ item | default('Unknown Service', true ) }}" -------------------------------------------------------------------------------- /ansible/host_vars/hashicorp: -------------------------------------------------------------------------------- 1 | # general variables 2 | home_dir: "{{ playbook_dir | dirname }}" 3 | stack_dir: "/home/ubuntu/boundary-vault-stack" 4 | compose_dir: "{{ stack_dir }}/deploy" 5 | vault_addr: "192.168.1.15:8200" 6 | boundary_addr: "192.168.1.15:9200" 7 | handlers: "{{ playbook_dir }}/handlers" 8 | log_dir: "{{ playbook_dir | dirname }}/logs" 9 | secret_dir : "{{stack_dir}}/secrets" 10 | 11 | # environment variables 12 | STACK_ENV: "{{ lookup('env', 'STACK_ENV') }}" 13 | STACK_INIT: "{{ lookup('env', 'STACK_INIT') }}" 14 | SSH_INJECTION: "{{lookup('env', 'SSH_INJECTION')}}" -------------------------------------------------------------------------------- /ansible/host_vars/localhost: -------------------------------------------------------------------------------- 1 | # general variables 2 | home_dir: "{{ playbook_dir | dirname }}" 3 | stack_dir: "/home/ubuntu/boundary-vault-stack" 4 | compose_dir: "{{ stack_dir }}/deploy" 5 | vault_addr: "127.0.0.1:8200" 6 | boundary_addr: "127.0.0.1:9200" 7 | handlers: "{{ playbook_dir }}/handlers" 8 | log_dir: "{{ playbook_dir | dirname }}/logs" 9 | secret_dir : "{{stack_dir}}/secrets" 10 | 11 | # environment variables 12 | STACK_ENV: "{{ lookup('env', 'STACK_ENV') }}" 13 | STACK_INIT: "{{ lookup('env', 'STACK_INIT') }}" 14 | SSH_INJECTION: "{{lookup('env', 'SSH_INJECTION')}}" -------------------------------------------------------------------------------- /ansible/inventory/inventory.ini: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 34356165313161356562656435373638353632373230316332326637663761393366346335306137 3 | 6466353634393666626538626366656238343065616562340a393866313064376137366139353236 4 | 33383062323839306236336461646634666533663739316266366337363533653937383632363133 5 | 3131383562353961620a396530613736313934653339656462336463386639303330346338633435 6 | 65313263633735356262313836316134346636383936653131333934363466626132616133343038 7 | 35616430333563626231363365666536323832383234646637636330353633636436366530363731 8 | 38383936663065633038666336303935313935303965656134373336353564343833316530633934 9 | 61383162396138643632616162323032656638646135633736343832376134316465353831383135 10 | 64646332316166323033383261306161386231356663383332656239386630396366346433646334 11 | 38663966306162663538623264376538616563333831653964303462623162366438373833636333 12 | 37666536303333643131613763326162663337643964653862303030326132373038326236316630 13 | 37626631643130633562653734386536626563653161363136376363353332376366383132666264 14 | 3266 15 | -------------------------------------------------------------------------------- /ansible/playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: prepare the stack environment and deploy vault 3 | hosts: hashicorp 4 | roles: 5 | - prepare_env 6 | - vault 7 | -------------------------------------------------------------------------------- /ansible/roles/boundary/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | STACK_INIT: true 3 | STACK_ENV: development 4 | SSH_INJECTION: false -------------------------------------------------------------------------------- /ansible/roles/boundary/tasks/boundary_iac.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run terraform configuration 3 | ansible.builtin.shell: HOME_DIR={{ home_dir }} VAULT_TOKEN={{ transit_token }} SSH_INJECTION={{SSH_INJECTION}} bash "{{home_dir}}/scripts/init.sh" boundary 4 | delegate_to: localhost 5 | ignore_errors: true 6 | register: terraform_boundary 7 | 8 | - name: Handle errors if occurred 9 | ansible.builtin.include_tasks: "{{handlers}}/fail_task.yml" 10 | vars: 11 | output: "{{ terraform_boundary }}" 12 | -------------------------------------------------------------------------------- /ansible/roles/boundary/tasks/deploy.yml: -------------------------------------------------------------------------------- 1 | - name: Get a list of Docker containers 2 | community.docker.docker_container_info: 3 | name : "{{ item }}" 4 | loop : "{{ containers }}" 5 | register: containers_info 6 | 7 | - name: Remove boundary containers if exists. 8 | community.docker.docker_container: 9 | name : "{{ item.container.Id }}" 10 | state: absent 11 | force_kill: yes 12 | with_items : 13 | - "{{ containers_info.results }}" 14 | when : item.exists 15 | loop_control: 16 | label: "{{ item.container.Name | default('Unkown Container', true) }}" 17 | 18 | - name: Remove boundary db volume if not in 'production' env 19 | become: true 20 | file: 21 | path: "{{ vol_path }}" 22 | state: absent 23 | when: STACK_ENV != 'production' 24 | register: vol_absent_result 25 | 26 | - name: Deploy boundary using deploy script 27 | become: true 28 | shell: VAULT_ADDR={{ vault_addr }} VAULT_TOKEN={{ transit_token }} STACK_DIR={{ stack_dir }} bash {{ deploy_script }} boundary 29 | ignore_errors: true 30 | register: boundary_deployment 31 | 32 | - name: handle errors if occurred 33 | include_tasks: "{{handlers}}/fail_task.yml" 34 | vars: 35 | output: "{{ boundary_deployment }}" 36 | 37 | - name: run the docker containers healthcheck 38 | include_tasks: "{{handlers}}/container_healthcheck.yml" 39 | vars: 40 | INFO: "{{ containers_info }}" 41 | -------------------------------------------------------------------------------- /ansible/roles/boundary/tasks/init_credentials.yml: -------------------------------------------------------------------------------- 1 | - name: fetch the logs for the service(s) 2 | shell: docker compose -f {{ service_path }} logs db-init --no-color | grep -A49 "Initial login" 3 | register: init_creds 4 | ignore_errors: true 5 | 6 | - name: Save credentials to a secrets.txt 7 | shell: echo -e "{{ init_creds.stdout | default(init_creds.stderr, true) }}" > "{{ secret_dir }}/boundary-init-creds.txt" 8 | 9 | - name: retrieve init secrets 10 | include_tasks: "{{playbook_dir}}/utils/secrets.yml" 11 | vars: 12 | secret_file: "boundary-init-creds.txt" 13 | timeout : 3 -------------------------------------------------------------------------------- /ansible/roles/boundary/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: deploy boundary 3 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/deploy.yml" 4 | - name: apply boundary terraform 5 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/boundary_iac.yml" 6 | - name: handle boundary db-init credentials 7 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/init_credentials.yml" 8 | -------------------------------------------------------------------------------- /ansible/roles/boundary/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | deploy_script: "{{ stack_dir }}/scripts/deploy.sh" 3 | containers: 4 | - boundary 5 | - boundary_db 6 | 7 | services: 8 | - db-init 9 | 10 | service_path: "{{stack_dir}}/deploy/boundary.yml" 11 | 12 | vol_path: /srv/boundary 13 | -------------------------------------------------------------------------------- /ansible/roles/cleanup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cleanup vault tokens 3 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/tokens.yml" 4 | - name: Secrets.txt related clean up ops 5 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/secrets_file.yml" 6 | -------------------------------------------------------------------------------- /ansible/roles/cleanup/tasks/secrets_file.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove the secrets.txt file form the target node(s) 3 | ansible.builtin.file: 4 | path: "{{ secret_file }}" 5 | state: absent 6 | -------------------------------------------------------------------------------- /ansible/roles/cleanup/tasks/tokens.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove transit token from boundary var file 3 | ansible.builtin.shell: HOME_DIR={{home_dir}} bash "{{home_dir}}/scripts/cleanup.sh" -d 4 | delegate_to: localhost 5 | -------------------------------------------------------------------------------- /ansible/roles/cleanup/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | secret_file: "{{stack_dir}}/secrets/secrets.txt" 3 | -------------------------------------------------------------------------------- /ansible/roles/prepare_env/tasks/docker_images.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Pull necessary docker images 3 | community.docker.docker_image: 4 | name: "{{ item.value.name }}" 5 | source: pull 6 | tag: "{{ item.value.tag }}" 7 | loop: "{{ docker_images | dict2items }}" 8 | async: 600 9 | poll: 0 10 | register: docker_pull_job 11 | 12 | - name: Extract job IDs 13 | ansible.builtin.set_fact: 14 | job_ids: "{{ docker_pull_job.results | map(attribute='ansible_job_id') | list }}" 15 | -------------------------------------------------------------------------------- /ansible/roles/prepare_env/tasks/files_and_directories.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Copy all files and directories 3 | become: true 4 | ansible.posix.synchronize: 5 | src: "{{ home_dir }}" 6 | dest: "{{ dest_dir }}" 7 | rsync_opts: 8 | # - "--exclude=.git/" 9 | - --exclude=.gitignore 10 | - --exclude=README.md 11 | - --exclude=ansible/ 12 | - --exclude=venv/ 13 | 14 | - name: Create main directory structure 15 | ansible.builtin.file: 16 | path: "{{ dest_dir }}" 17 | state: directory 18 | mode: "0755" 19 | -------------------------------------------------------------------------------- /ansible/roles/prepare_env/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: pull required docker images 3 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/docker_images.yml" 4 | 5 | - name: copy and sync files and directories 6 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/files_and_directories.yml" 7 | -------------------------------------------------------------------------------- /ansible/roles/prepare_env/vars/main.yml: -------------------------------------------------------------------------------- 1 | dest_dir: /home/ubuntu/ 2 | 3 | docker_images: 4 | vault: 5 | name: hashicorp/vault 6 | tag: 1.13.3 7 | postgresql: 8 | name: bitnami/postgresql 9 | tag: 16 10 | boundary: 11 | name: hashicorp/boundary 12 | tag: 0.14 13 | busybox: 14 | name: busybox 15 | tag: latest -------------------------------------------------------------------------------- /ansible/roles/provision/default/main.yml: -------------------------------------------------------------------------------- 1 | # handle default values for provisioners here. -------------------------------------------------------------------------------- /ansible/roles/provision/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # Implement Different Provisioners on seperated playbooks and include them here 2 | -------------------------------------------------------------------------------- /ansible/roles/provision/vars/main.yml: -------------------------------------------------------------------------------- 1 | ### handle variable for different provisioners seperate by #[Provisioner_Name]. 2 | 3 | ### for instance : # [Vagrant] 4 | ### some_task: -------------------------------------------------------------------------------- /ansible/roles/terraform/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | SSH_INJECTION: Flase 3 | -------------------------------------------------------------------------------- /ansible/roles/terraform/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart_ssh 3 | become: true 4 | ansible.builtin.service: 5 | name: sshd 6 | state: restarted 7 | -------------------------------------------------------------------------------- /ansible/roles/terraform/tasks/cred_store_ssh.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure ssh public_key from vault 3 | become: true 4 | ansible.builtin.shell: cp {{stack_dir}}/secrets/ca-key.pub /etc/ssh/ca-key.pub && chown 1000:1000 /etc/ssh/ca-key.pub && chmod 644 /etc/ssh/ca-key.pub && echo "TrustedUserCAKeys 5 | /etc/ssh/ca-key.pub" >> /etc/ssh/sshd_config 6 | notify: restart_ssh 7 | when: SSH_INJECTION == True 8 | 9 | - name: Add ssh cred store token to variables 10 | ansible.builtin.shell: bash "{{home_dir}}/scripts/cleanup.sh" ssh 11 | delegate_to: localhost 12 | when: SSH_INJECTION == True 13 | -------------------------------------------------------------------------------- /ansible/roles/terraform/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: credential store token ssh preparation 3 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/cred_store_ssh.yml" 4 | - name: handle vault transit in tfvars 5 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/tfvars_token.yml" 6 | - name: apply terraform for vault 7 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/vault_iac.yml" 8 | -------------------------------------------------------------------------------- /ansible/roles/terraform/tasks/tfvars_token.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add transit token to boundary tfvars file 3 | ansible.builtin.shell: HOME_DIR={{home_dir}} bash "{{home_dir}}/scripts/cleanup.sh" 4 | delegate_to: localhost 5 | ignore_errors: true 6 | register: tfvars_token_injection 7 | 8 | - name: Handle errors if occurred 9 | ansible.builtin.include_tasks: "{{handlers}}/fail_task.yml" 10 | vars: 11 | output: "{{ tfvars_token_injection }}" 12 | -------------------------------------------------------------------------------- /ansible/roles/terraform/tasks/vault_iac.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run vault terraform configuration 3 | ansible.builtin.shell: HOME_DIR={{home_dir}} VAULT_ADDR={{VAULT_ADDRESS}} bash "{{home_dir}}/scripts/init.sh" vault 4 | delegate_to: localhost 5 | ignore_errors: true 6 | register: vault_terraform 7 | 8 | - name: Handle errors if occurred 9 | ansible.builtin.include_tasks: "{{handlers}}/fail_task.yml" 10 | vars: 11 | output: "{{ vault_terraform }}" 12 | -------------------------------------------------------------------------------- /ansible/roles/terraform/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | VAULT_ADDRESS: http://192.168.1.15:8200 3 | -------------------------------------------------------------------------------- /ansible/roles/vault/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | STACK_INIT: true 3 | STACK_ENV: development 4 | secret_file: secrets.txt 5 | -------------------------------------------------------------------------------- /ansible/roles/vault/tasks/deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create hcp external 3 | become: true 4 | community.docker.docker_network: 5 | name: hashicorp 6 | 7 | - name: Remove vault volume if not in 'production' env 8 | become: true 9 | ansible.builtin.file: 10 | path: "{{ vol_path }}" 11 | state: absent 12 | when: STACK_ENV != 'production' 13 | register: vol_absent_result 14 | 15 | - name: Get a list of Docker containers 16 | community.docker.docker_container_info: 17 | name: "{{ item }}" 18 | loop: "{{ containers }}" 19 | register: containers_info 20 | 21 | - name: Remove vault containers if exists 22 | community.docker.docker_container: 23 | name: "{{ item.container.Id }}" 24 | state: absent 25 | force_kill: true 26 | with_items: 27 | - "{{ containers_info.results }}" 28 | loop_control: 29 | label: "ID : {{ item.container.Id | default('Unkown Container', true) }} , NAME: {{ item.container.Name | default('Unkown Container', true) }}" 30 | when: item.exists 31 | 32 | - name: Deploy vault using deploy_script 33 | become: true 34 | ansible.builtin.shell: STACK_DIR={{ stack_dir }} STACK_INIT={{ STACK_INIT }} bash {{ deploy_script }} vault 35 | ignore_errors: true 36 | register: vault_deployment 37 | 38 | - name: Handle errors if occurred 39 | ansible.builtin.include_tasks: "{{handlers}}/fail_task.yml" 40 | vars: 41 | output: "{{ vault_deployment }}" 42 | 43 | - name: Get a list of Docker containers 44 | community.docker.docker_container_info: 45 | name: "{{ item }}" 46 | loop: "{{ containers }}" 47 | register: containers_info 48 | 49 | - name: run the docker containers healthcheck 50 | ansible.builtin.include_tasks: "{{handlers}}/container_healthcheck.yml" 51 | vars: 52 | INFO: "{{ containers_info }}" 53 | -------------------------------------------------------------------------------- /ansible/roles/vault/tasks/docker_pull_status.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check docker pull status 3 | ansible.builtin.async_status: 4 | jid: "{{ item }}" 5 | register: pull_result 6 | until: pull_result.finished 7 | retries: 10 8 | delay: 60 9 | loop: "{{job_ids}}" 10 | 11 | - name: Clean up async file 12 | ansible.builtin.async_status: 13 | jid: "{{ item }}" 14 | mode: cleanup 15 | loop: "{{job_ids}}" 16 | -------------------------------------------------------------------------------- /ansible/roles/vault/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check the status of docker images jobs 3 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/docker_pull_status.yml" 4 | 5 | - name: deploy vault 6 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/deploy.yml" 7 | 8 | - name: retrieve vault secrets 9 | ansible.builtin.include_tasks: "{{ role_path }}/tasks/secrets.yml" 10 | -------------------------------------------------------------------------------- /ansible/roles/vault/tasks/secrets.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Make sure the old secret file is absent 3 | become: true 4 | ansible.builtin.file: 5 | path: "{{ home_dir }}/secrets/{{ secret_file }}" 6 | state: absent 7 | delegate_to: localhost 8 | 9 | - name: Wait for secret file to get generated 10 | ansible.builtin.wait_for: 11 | timeout: 10 12 | 13 | - name: Fetch the secret file to control node 14 | become: true 15 | ansible.builtin.fetch: 16 | src: "{{ secret_dir }}/{{ secret_file }}" 17 | dest: "{{ home_dir }}/secrets/{{ secret_file }}" 18 | flat: true 19 | fail_on_missing: true 20 | 21 | - name: Print at the secret file path 22 | ansible.builtin.debug: 23 | msg: You can now see Vault secrets at `{{ home_dir }}/secrets/{{ secret_file }}` 24 | -------------------------------------------------------------------------------- /ansible/roles/vault/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | deploy_script: "{{ stack_dir }}/scripts/deploy.sh" 3 | containers: 4 | - vault 5 | 6 | vol_path: /srv/vault 7 | -------------------------------------------------------------------------------- /ansible/terraform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name : ensure terraform requirments and apply tf modules 3 | hosts: hashicorp 4 | roles: 5 | - terraform -------------------------------------------------------------------------------- /ansible/utils/secrets.yml: -------------------------------------------------------------------------------- 1 | - name: make sure the old secret file is absent 2 | become: true 3 | ansible.builtin.file: 4 | path: "{{home_dir}}/secrets/{{ secret_file }}" 5 | state: absent 6 | delegate_to : localhost 7 | 8 | - name: wait for secret file to get generated 9 | wait_for: 10 | timeout: "{{timeout}}" 11 | 12 | - name: fetch the secret file to control node 13 | become: true 14 | fetch: 15 | src: "{{ secret_dir }}/{{ secret_file }}" 16 | dest: "{{home_dir}}/secrets/{{ secret_file }}" 17 | flat: yes 18 | fail_on_missing: true 19 | 20 | - name : Print at the secret file path 21 | debug: 22 | msg: "You can now see the secrets at `{{home_dir}}/secrets/{{ secret_file }}`" -------------------------------------------------------------------------------- /artifacts/diagrams/boundary.py: -------------------------------------------------------------------------------- 1 | from diagrams import Diagram, Cluster, Edge, Node 2 | from diagrams.onprem.compute import Server 3 | from diagrams.onprem.auth import Boundary 4 | from diagrams.onprem.security import Vault 5 | from diagrams.onprem.database import PostgreSQL 6 | from diagrams.oci.governance import Audit, Logging 7 | 8 | 9 | graph_attr = { 10 | "fontname": "Roboto", 11 | "fontsize": "24" 12 | } 13 | 14 | with Diagram("Boundary server Workflow", show=False, direction="LR", graph_attr=graph_attr, filename="boundary"): 15 | boundary_controller = Boundary("Boundary Controller") 16 | boundary_worker = Boundary("Boundary Worker") 17 | vault = Vault("Vault Transit Engine") 18 | 19 | with Cluster("Listeners"): 20 | api_listener = Server("API Listener") 21 | cluster_listener = Server("Cluster Listener") 22 | proxy_listener = Server("Proxy Listener") 23 | Node(label="", width="2", height="0", style="invisible") 24 | 25 | 26 | with Cluster("Audit Event Sinks"): 27 | audit_file_sink = Audit("Controller") 28 | auth_sink = Audit("Auth Observation") 29 | session_sink = Audit("Session Authorization") 30 | Node(label="", width="2", height="0", style="invisible") 31 | stderr_sink = Logging("Stderr Sink") 32 | 33 | with Cluster("KMS Keys"): 34 | recovery_key = Boundary("recovery") 35 | worker_auth = Boundary("worker-ath") 36 | root_key = Boundary("root") 37 | 38 | postgres= PostgreSQL("Postgresql") 39 | 40 | # Controller connections 41 | boundary_controller >> Edge(label="TCP connection") >> cluster_listener 42 | boundary_controller >> Edge(label="Audit File Events") >> auth_sink 43 | boundary_controller >> Edge(label="All-events") >> stderr_sink 44 | 45 | # Worker connections 46 | boundary_worker >> Edge(label="Connected to Controller") >> boundary_controller 47 | 48 | # KMS connections 49 | worker_auth >> Edge() >> vault 50 | recovery_key >> Edge() >> vault 51 | root_key >> Edge() >> vault 52 | root_key << Edge(attrs="penwidth: 2.0") << boundary_controller 53 | 54 | # DB connections 55 | postgres << Edge(label="DB Connection") << boundary_controller -------------------------------------------------------------------------------- /artifacts/diagrams/vault.py: -------------------------------------------------------------------------------- 1 | from diagrams import Diagram, Cluster, Edge, Node 2 | from diagrams.onprem.security import Vault 3 | from diagrams.generic.storage import Storage 4 | from diagrams.onprem.client import Users 5 | 6 | with Diagram("\nVault Server Workflow", show=False, direction="RL", graph_attr={"fontname" : "arial", "fontsize": "28" }, filename="vault"): 7 | cluster_attr= { 8 | "margin" : "20", 9 | "fontsize": "16", 10 | "fontname" : "arial"} 11 | 12 | with Cluster("Vault Setup", graph_attr=cluster_attr): 13 | vault_listener = Vault("TCP Listener") 14 | storage_raft = Storage("\nRaft Storage") 15 | vault_ui = Vault("UI") 16 | 17 | with Cluster("User Management", graph_attr=cluster_attr): 18 | userpass_lockout = Users("\nUserpass Lockout") 19 | users = Users("\nUsers") 20 | 21 | # Vault connections 22 | vault_listener - Edge(label="0.0.0.0:8200\nTLS Disabled") >> vault_ui 23 | vault_listener >> Edge(label="Max Entry Size\n1MB") >> storage_raft 24 | 25 | # User Management connections 26 | users - Edge(label="Lockout Threshold: 3\nLockout Duration: 10m") - userpass_lockout 27 | 28 | # External connections 29 | api_addr = Vault("API Address\nhttp://localhost:8200") 30 | cluster_addr = Vault("Cluster Address\nhttp://127.0.0.1:8201") 31 | 32 | vault_listener >> Edge(label="API and Cluster Addresses") >> [api_addr, cluster_addr] 33 | -------------------------------------------------------------------------------- /artifacts/wiki.md: -------------------------------------------------------------------------------- 1 | # Boundary-Vault-Stack 2 | 3 | ## Table of Contents 4 | - [Getting Started](#getting-started) 5 | - [About Hashicorp Vault and Boundary](#about-hashicorp-vault-and-boundary) 6 | - [Workflows](#workflows) 7 | - [Vault](#vault) 8 | - [Boundary](#boundary) 9 | - [Configurations](#configurations) 10 | - [Environment Variables](#environment-variables) 11 | - [Return/Exit Codes](#returnexit-codes) 12 | - [Bear In Mind](#bear-in-mind) 13 | 14 | 15 | ## Getting started 16 | After the server is properly provision and configured you'll have Vault and Boundary up and running. For the sake of education the stack will be initialized with minimum resources for both services including KV and Transit engine (vault) and a series of auth-method, host catalog, credential stores, etc (Boundary). As the Contributions increase, the resources will be enriched accordingly covering more arbitrary resources and features in the format of IaC. 17 | 18 | To grasp what's going on under the hood, you can reach out to the section you wish to explore in this documentation. 19 | 20 | 21 | ## About Hashicorp Vault and Boundary 22 | 23 | According to Hashicorp documentation, 24 | 25 | [Boundary](https://developer.hashicorp.com/boundary/docs/overview/what-is-boundary) is an identity-aware proxy that simplifies and secures least-privileged access to cloud infrastructure. It enables SSO, just-in-time access, dynamic credentials, and session management. 26 | 27 | [Vault](https://developer.hashicorp.com/vault/docs/what-is-vault) is an identity-based secrets and encryption management system. A secret is anything that you want to tightly control access to, such as API encryption keys, passwords, and certificates. Vault provides encryption services that are gated by authentication and authorization methods. Using Vault’s UI, CLI, or HTTP API, access to secrets and other sensitive data can be securely stored and managed, tightly controlled (restricted), and auditable. 28 | 29 | ### learn more: 30 | - [Boundary](https://youtu.be/tUMe7EsXYBQ?si=3IFGbMLGTEy_3X1T) 31 | - [Vault](https://youtu.be/VYfl-DpZ5wM?si=a3Ign_zoUNS92EAP) 32 | 33 | ## Workflows 34 | ### Vault 35 | 36 | 37 | 38 | ### Boundary 39 | 40 | 41 | 42 | ## Configurations 43 | 44 | ### Environment Variables 45 | 46 | #### STACK_ENV (mandatory) 47 | **Description**: This variable determies in which mode/environment the stack is deployed. 48 | 49 | **Options**: 50 | - development 51 | - test 52 | - staging 53 | - production 54 | 55 | **default**: development 56 | 57 | --- 58 | 59 | #### STACK_INIT (mandatory) 60 | 61 | **Description**: When **first** running the stack, `vault-init` and `boundary-init` services are in charge of initiating the basic configurations for `boundary` and `vault`. This variable determines wether this services should be executed or not. So if it's **not** your first time running the stack successfully, set to `false`. 62 | 63 | **Options**: 64 | - true 65 | - false 66 | 67 | **default**: true 68 | 69 | --- 70 | 71 | #### SSH_INJECTION (optional) 72 | 73 | **Description**: 74 | 75 | **Options**: 76 | - true 77 | - false 78 | 79 | ## Return/Exit Codes 80 | 81 | In this project, several scripts use return/exit codes to indicate the result of operations. Understanding these codes is essential for diagnosing issues and ensuring proper execution of the scripts. Below is a detailed explanation of each return/exit code used in the project. 82 | 83 | ### Exit Code 1: Service Not Installed 84 | 85 | **Description**: This exit code indicates that the required service is not installed on the system. 86 | 87 | **Possible Causes**: 88 | - The service was not installed during the setup process. 89 | - The installation process was interrupted or failed. 90 | 91 | **Resolution**: 92 | - Verify and Ensure that the installation was successful by running the `prepare_env` role. 93 | 94 | **Example**: 95 | ```bash 96 | scripts/init.sh vault 97 | # Output: Terraform not installed 98 | # Exit code: 1 99 | ``` 100 | 101 | --- 102 | 103 | ### Exit Code 2: Terraform Init Failed 104 | 105 | **Description**: This exit code indicates that the `terraform init` command failed. 106 | 107 | **Possible Causes**: 108 | - The Terraform configuration files are missing or corrupted. 109 | - There is a network issue preventing Terraform from accessing necessary modules or providers. 110 | - Incorrect permissions to the directory where Terraform is being initialized. 111 | - Wrong terraform directory path 112 | 113 | **Resolution**: 114 | - Ensure that all required Terraform configuration files are present and correctly configured. 115 | - Check network connectivity and permissions. 116 | - Refer to the [Terraform Documentation](https://www.terraform.io/docs/commands/init.html) for more details. 117 | 118 | **Example**: 119 | ```bash 120 | scripts/init.sh vault 121 | # Output: Terraform init failed 122 | # Exit code: 2 123 | ``` 124 | 125 | --- 126 | 127 | ### Exit Code 3: Configuration is Invalid 128 | 129 | **Description**: This exit code indicates that `terrafrom validate` was not successfuly executed. 130 | 131 | **Possible Causes**: 132 | - The configuration file has syntax errors. 133 | - Required configuration parameters are missing or incorrect. 134 | 135 | **Resolution**: 136 | - Validate the configuration file against the expected schema. 137 | - Ensure all required parameters are correctly specified. 138 | 139 | **Example**: 140 | ```bash 141 | scripts/init.sh vault 142 | # Output: Configuration is invalid 143 | # Exit code: 3 144 | ``` 145 | --- 146 | 147 | ### Exit Code 4: Arguments and Options are Invalid 148 | 149 | **Description**: This exit code indicates that the arguments or options passed to the script are invalid. 150 | 151 | **Possible Causes**: 152 | - Incorrect or missing arguments/options. 153 | - The script was invoked with unsupported options. 154 | 155 | **Resolution**: 156 | - Refer to the script usage documentation to ensure all required arguments and options are correctly specified. 157 | - Use the `--help` option with the script to view the correct usage. 158 | 159 | **Example**: 160 | ```bash 161 | ./start.sh --environment development 162 | # Output: Invalid option: --environment 163 | # Exit code: 4 164 | ``` 165 | 166 | ## Bear In Mind 167 | - If you have issues with DockerHub make sure you change the image registry in deployments and `prepare_env` role. 168 | 169 | - If the target node(s) get restarted, the `vault` gets sealed and `boundary` container will be in restarting mode. 170 | 171 | - In case the `vault` container gets restarted, it will be sealed and you'll have an error on your `boudary` container, There manage to get them working together again. 172 | 173 | - You can additionally add session recording and other paid plan features. 174 | 175 | - Vault is initialized with 1 shared-key to simplify the process, consider increasing the number of keys and threshold for better security. 176 | 177 | ## Still Having Issues? 178 | 179 | For further assistance, feel free to open up a new issue on the [GitHub Issues page](https://github.com/devopshobbies/boundary-vault-stack/issues). 180 | -------------------------------------------------------------------------------- /artifacts/wiki/app.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('scroll', function () { 2 | const headers = document.querySelectorAll('.section-header'); 3 | const pageOffset = window.scrollY; 4 | 5 | headers.forEach(header => { 6 | const headerOffset = header.offsetTop; 7 | const headerHeight = header.offsetHeight; 8 | 9 | if (pageOffset >= headerOffset - headerHeight && pageOffset < headerOffset + headerHeight) { 10 | header.classList.add('active'); 11 | } else { 12 | header.classList.remove('active'); 13 | } 14 | }); 15 | }); 16 | 17 | function copyToClipboard() { 18 | const codeElement = document.querySelector('.code'); 19 | const range = document.createRange(); 20 | range.selectNode(codeElement); 21 | window.getSelection().removeAllRanges(); 22 | window.getSelection().addRange(range); 23 | document.execCommand('copy'); 24 | window.getSelection().removeAllRanges(); 25 | 26 | 27 | const notification = document.getElementById('copy-notification'); 28 | notification.classList.add('show'); 29 | 30 | setTimeout(() => { 31 | notification.classList.remove('show'); 32 | }, 3000); 33 | } -------------------------------------------------------------------------------- /artifacts/wiki/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Boundary-Vault-Stack Documentation 8 | 9 | 10 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | Boundary-Vault-Stack Documentation 25 |
26 | 27 |
28 | 48 | 49 |
50 |
51 |

Getting Started

52 |

After the server is properly provisioned and configured, you'll have Vault and Boundary up and 53 | running. 54 | For the sake of education, the stack will be initialized with minimum resources for both services, 55 | including KV and Transit engine Vault and a series of auth-methods, host 56 | catalog, credential 57 | stores, etc. Boundary. As the contributions increase, the resources will 58 | be enriched accordingly, covering 59 | more arbitrary resources and features in the format of Infrastructure as Code (IaC).

60 |

To grasp what's going on under the hood, you can reach out to the section you wish to explore in this 61 | documentation.

62 | 63 |

About Hashicorp Vault and Boundary 64 |

65 |

According to Hashicorp documentation:

66 |

Boundary is an 67 | identity-aware proxy that simplifies and secures least-privileged access to cloud infrastructure. It 68 | enables SSO, just-in-time access, dynamic credentials, and session management.

69 |

Vault is an identity-based 70 | secrets 71 | and encryption management system. A secret is anything that you want to tightly control access to, 72 | such 73 | as API encryption keys, passwords, and certificates. Vault provides encryption services that are 74 | gated 75 | by authentication and authorization methods. Using Vault’s UI, CLI, or HTTP API, access to secrets 76 | and 77 | other sensitive data can be securely stored, managed, tightly controlled (restricted), and audited. 78 |

79 |

Learn more:

80 | 84 |
85 | 86 |
87 |

Workflow

88 |

After the environment is prepared, Ansible deploys Vault using Docker Compose. The Vault server is 89 | first initialized by the vault-init service, which runs the `init.sh` 90 | script and calls the `init_vault_setup` function. This process unseals Vault, creates the necessary 91 | secret engines, policies, tokens, and other basic configurations. Once the deployment is complete, the 92 | Transit-token and secrets.txt file, containing 93 | all the essential tokens and keys, are generated and stored in the `secrets/secrets.txt` file on the 94 | controller machine. 95 | 96 | Next, the Terraform role is applied to configure Vault, utilizing the generated tokens and 97 | variables. Following this, the deployment shifts focus to Boundary. To enable Boundary to use 98 | Vault's Transit Engine for Encryption as a Service (EaaS), the Transit token (already stored in 99 | `secrets.txt`) is passed to the Boundary role. Once the Boundary database (PostgreSQL) is up and 100 | running, it is initialized using the db-init service. Finally, Boundary is 101 | deployed with the initial credentials saved in secrets/boundary-init-creds.txt. Terraform resources are then applied using 103 | the Vault Transit recovery key and Transit 104 | token. 105 | 106 | To use the stack, you need to install the Boundary and Vault clients (ensure the server and client 107 | versions match). Once installed, you can start using BVSTACK. 108 | 109 | You can checkout the boundary UI at http://192.168.1.15:9200 and Vault at http://192.168.1.15:8200 . 110 | 111 |

112 | 113 |

Vault

114 |

Vault workflow involves setting up authentication methods, secret engines, and policies. The key 115 | components of Vault server setup include:

116 | vault diagram 117 | 118 |

Boundary

119 |

Boundary workflow involves managing sessions, targets, and credentials. The key 120 | components of Boundary server setup include: 121 |

122 | vault diagram 123 | 124 |
125 | 126 | 127 |
128 |

Configurations

129 |

Environment Variables

130 |

Environment variables need to be configured for both Vault and Boundary. Here's an example:

131 |

STACK_ENV (mandatory)

132 |

This variable determines in which mode/environment the stack is deployed.

133 |
    134 |
  • development
  • 135 |
  • test
  • 136 |
  • staging
  • 137 |
  • production
  • 138 |
139 |

default : development

140 |
141 |

STACK_INIT (mandatory)

142 |

When first running the stack, vault-init and 143 | boundary-init services are in charge of initiating the basic 144 | configurations for Boundary and 145 | `vault`. This variable determines whether these services should be executed or not. So if it's not your first time running the stack successfully, set to false. 148 |

149 |
    150 |
  • true
  • 151 |
  • false
  • 152 |
153 |

default : true

154 |
155 |

SSH_INJECTION (optional)

156 |

SSH injection variable enables Boundary vault credential store. only works on paid 157 | plan!!

158 |
    159 |
  • true
  • 160 |
  • false
  • 161 |
162 |

default : false

163 | 164 |

STACK_SERVER (optional)

165 |

If set to false, vagrant and virtualbox won't be used to spin up BVSTACK. Instead you must create 166 | both Controller, BVSTACK and Client machines manually using your prefered 167 | method; ensure to address them in the inventory file accordingly.

168 |
    169 |
  • true
  • 170 |
  • false
  • 171 |
172 |

default : true

173 | 174 |
175 | 176 |
177 |

Return/Exit Codes

178 |

In this project, several scripts use return/exit codes to indicate the result of operations. 179 | Understanding these codes is essential for diagnosing issues and ensuring proper execution of the 180 | scripts. Below is a detailed explanation of each return/exit code used in the project.

181 | 182 |

Exit Code 1: Service Not Installed

183 |

Description: This exit code indicates that the required service is not 184 | installed on the system.

185 |

Possible Causes:

186 |
    187 |
  • The service was not installed during the setup process.
  • 188 |
  • The installation process was interrupted or failed.
  • 189 |
190 |

Resolution:

191 |
    192 |
  • Verify and ensure that the installation was successful by running the prepare_env role.
  • 194 |
195 | 196 |

Resolution:

197 |
198 | 201 |
$ scripts/init.sh vault
202 | 
203 | # Output: Terraform not installed
204 | # Exit code: 1
205 | 
206 |                     
207 |
208 |
Copied!
209 | 210 |

Exit Code 2: Terraform Init Failed

211 |

Description: This exit code indicates that the `terraform init` command 212 | failed.

213 |

Possible Causes:

214 |
    215 |
  • The Terraform configuration files are missing or 216 | 217 | corrupted.
  • 218 |
  • There is a network issue preventing Terraform from accessing necessary modules or providers. 219 |
  • 220 |
  • Incorrect permissions to the directory where Terraform is being initialized.
  • 221 |
  • Wrong terraform directory path.
  • 222 |
223 |

Resolution:

224 |
    225 |
  • Ensure that all required Terraform configuration files are present and correctly configured. 226 |
  • 227 |
  • Refer to the Terraform Documentation for more details.
  • 229 |
  • Check network connectivity and permissions.
  • 230 |
231 | 232 |

Resolution:

233 |
234 | 237 |
238 | $ scripts/init.sh vault
239 | 
240 | # Output: Terraform init failed
241 | # Exit code: 2
242 | 
243 | 
244 |
245 |

Exit Code 3: Configuration is Invalid

246 |

Description: This exit code indicates that `terraform validate` was not 247 | successfully executed.

248 |

Possible Causes:

249 |
    250 |
  • The configuration file has syntax errors.
  • 251 |
  • Required configuration parameters are missing or incorrect.
  • 252 |
253 |

Resolution:

254 |
    255 |
  • Validate the configuration file against the expected schema.
  • 256 |
  • Ensure all required parameters are correctly specified.
  • 257 |
258 | 259 |

Resolution:

260 |
261 | 264 |
265 | $ scripts/init.sh vault
266 | 
267 | # Output: Configuration is invalid
268 | # Exit code: 3
269 | 
270 | 
271 |
272 |

Exit Code 4: Arguments and Options are Invalid.

273 |

Description: This exit code indicates that the arguments or options passed 274 | to the script are invalid.

275 |

Possible Causes:

276 |
    277 |
  • Incorrect or missing arguments/options.
  • 278 |
  • The script was invoked with unsupported options.
  • 279 |
280 |

Resolution:

281 |
    282 |
  • Refer to the script usage documentation to ensure all required arguments and options are 283 | correctly specified.
  • 284 |
  • Use the `--help` option with the script to view the correct usage.
  • 285 |
286 | 287 |

Resolution:

288 |
289 | 292 |
293 | $ ./start.sh --environment development
294 | 
295 | # Output: Invalid option: --environment
296 | # Exit code: 4
297 | 
298 | 
299 |
300 |
301 | 302 |
303 |

Bear In Mind

304 |

Keep the following in mind when working with the Boundary-Vault stack:

305 |
    306 |
  • Since Boundary Terraform uses Vault recovery transit key you must export the transit-token as 307 | VAULT_TOKEN 308 | (secrets/secrets.txt) before planning/applying the code. Otherwise, 309 | you should use auth-method credentials to communicate with the boundary api. 310 |
  • 311 |
  • If you have issues with DockerHub make sure you change the image registry in deployments and 312 | `prepare_env` role.
  • 313 |
  • If the target node(s) get restarted, the vault gets sealed and boundary container will be in 315 | restarting mode.
  • 316 |
  • In case the vault container gets restarted, it will be sealed and 317 | you'll have an error on your 318 | boundary container, there manage to get them working together again. 319 |
  • 320 |
  • You can additionally add session recording and other paid plan features.
  • 321 |
  • Vault is initialized with 1 shared-key to simplify the process, consider increasing the number 322 | of keys and threshold for better security.
  • 323 |
324 |
325 | 326 |
327 |

Still Having Issues

328 |

For further assistance, feel free to open up a new issue on the GitHub Issues page.

330 |
331 | 332 |
333 |
334 | 335 | 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /artifacts/wiki/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --bold-default: 800; 3 | --bold-text: 500; 4 | --bg-primary: #f9f9f9; 5 | --clr-text: #262626; 6 | --clr-primary: #2b3e50; 7 | --clr-secondary: #007acc; 8 | --clr-header: #fff; 9 | --code-primary-clr: #33333344; 10 | --clr-code-light: #ffffffba; 11 | --font-primary: 'Roboto', sans-serif; 12 | --font-code: 'Fira Code', monospace; 13 | --font-size-large: 1.6rem; 14 | --font-size-mid: 1.4rem; 15 | --font-size-small: 1rem; 16 | --font-size-phone: 1.2rem; 17 | --margin-small: 1.6rem; 18 | --margin-mid: 3rem; 19 | --margin-large: 5rem; 20 | } 21 | 22 | html { 23 | scroll-behavior: smooth; 24 | } 25 | 26 | body { 27 | font-family: var(--font-primary); 28 | margin: 0; 29 | padding: 0; 30 | background-color: var(--bg-primary); 31 | color: var(--clr-text); 32 | line-height: 2.5rem; 33 | } 34 | 35 | header { 36 | background-color: var(--clr-primary); 37 | color: var(--clr-header); 38 | padding: 1rem; 39 | text-align: center; 40 | font-size: var(--font-size-mid); 41 | font-weight: 700; 42 | } 43 | 44 | .container { 45 | display: flex; 46 | justify-content: center; 47 | align-items: center; 48 | padding: 20px; 49 | font-size: var(--font-size-mid); 50 | } 51 | 52 | nav { 53 | flex: 1; 54 | align-self: self-start; 55 | position: sticky; 56 | top: 20px; 57 | max-width: 200px; 58 | font-size: var(--font-size-small); 59 | } 60 | 61 | nav ul { 62 | list-style-type: none; 63 | padding: 0; 64 | } 65 | 66 | nav ul li a { 67 | text-decoration: none; 68 | color: var(--clr-primary); 69 | padding: 8px 0; 70 | display: block; 71 | font-weight: var(--bold-text); 72 | } 73 | 74 | nav ul li a:hover { 75 | color: var(--clr-secondary); 76 | } 77 | 78 | .content { 79 | flex: 3; 80 | padding: 0 20px; 81 | } 82 | 83 | h2 { 84 | color: var(--clr-primary); 85 | font-size: 1.75rem; 86 | margin-top: 1rem; 87 | } 88 | 89 | h3 { 90 | color: var(--clr-primary); 91 | font-size: 1.5rem; 92 | margin-top: 1rem; 93 | } 94 | 95 | 96 | a { 97 | color: var(--clr-secondary); 98 | text-decoration: none; 99 | } 100 | 101 | a:hover { 102 | text-decoration: underline; 103 | } 104 | 105 | /* text style */ 106 | .bold { 107 | font-weight: var(--bold-text); 108 | } 109 | 110 | .i { 111 | background-color: var(--code-primary-clr); 112 | padding: 2px 7px; 113 | /* border-radius: 5px; */ 114 | } 115 | 116 | .default { 117 | font-weight: var(--bold-default); 118 | } 119 | 120 | 121 | 122 | /* code style */ 123 | 124 | .code { 125 | margin: 0; 126 | } 127 | 128 | .code-container { 129 | position: relative; 130 | background-color: var(--clr-primary); 131 | color: var(--clr-header); 132 | padding: 15px 20px; 133 | border-radius: 10px; 134 | font-family: var(--font-code); 135 | font-size: var(--font-size-small); 136 | /* overflow-x: auto; */ 137 | cursor: pointer; 138 | max-width: 768px; 139 | height: 160px; 140 | } 141 | 142 | .copy-btn { 143 | background-color: transparent; 144 | position: absolute; 145 | top: 10px; 146 | right: 10px; 147 | border: none; 148 | border-radius: 5px; 149 | padding: 5px 10px; 150 | cursor: pointer; 151 | font-size: var(--font-size-mid); 152 | transition: color 0.3s; 153 | color: var(--clr-header); 154 | } 155 | 156 | .copy-btn:hover { 157 | /* Change to your desired hover color */ 158 | color: var(--clr-secondary); 159 | /* Change to your desired text color on hover */ 160 | } 161 | 162 | #copy-notification { 163 | position: fixed; 164 | bottom: -50px; 165 | /* Start off-screen */ 166 | left: 50%; 167 | transform: translateX(-50%); 168 | background-color: var(--clr-secondary); 169 | color: var(--bg-primary); 170 | padding: 10px 20px; 171 | border-radius: 5px; 172 | font-size: 14px; 173 | opacity: 0; 174 | transition: bottom 0.5s ease, opacity 0.5s ease; 175 | z-index: 1000; 176 | } 177 | 178 | #copy-notification.show { 179 | bottom: 30px; 180 | opacity: 1; 181 | } 182 | 183 | .code span { 184 | color: var(--clr-code-light); 185 | } 186 | 187 | /* end of code styling */ 188 | 189 | 190 | /* scroll animation */ 191 | .section-header { 192 | padding-top: 20px; 193 | transition: color 0.3s ease, background-color 0.3s ease; 194 | color: var(--clr-primary); 195 | } 196 | 197 | .section-header.active { 198 | color: var(--clr-secondary); 199 | } 200 | 201 | /* end of scroll animation */ 202 | 203 | .rc .code-container { 204 | margin-bottom: var(--margin-small); 205 | } 206 | 207 | /* workflows */ 208 | .workflows { 209 | display: flex; 210 | flex-direction: column; 211 | max-width: 100%; 212 | margin: 0 auto; 213 | padding: 20px; 214 | } 215 | .workflows img { 216 | width: 100%; 217 | max-width: 700px; 218 | height: auto; 219 | margin-bottom: 20px; 220 | object-fit: contain; 221 | border-radius: 8px; 222 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 223 | align-self: center; 224 | } 225 | 226 | 227 | 228 | /* responsive */ 229 | 230 | @media only screen and (max-width: 768px) { 231 | .container { 232 | line-height: 3rem; 233 | font-size: var(--font-size-phone); 234 | } 235 | 236 | nav { 237 | display: none; 238 | } 239 | 240 | header { 241 | font-size: var(--font-size-mid); 242 | 243 | } 244 | 245 | .code { 246 | padding: 10px 10px; 247 | font-size: var(--font-size-small); 248 | overflow-x: auto; 249 | width: 300px; 250 | line-height: var(--font-size-large); 251 | } 252 | 253 | .content { 254 | padding: 30px 0; 255 | } 256 | } -------------------------------------------------------------------------------- /boundary/config/boundary.hcl: -------------------------------------------------------------------------------- 1 | # Copyright (c) HashiCorp, Inc. 2 | # SPDX-License-Identifier: MPL-2.0 3 | 4 | # make it false in prod 5 | disable_mlock = true 6 | 7 | controller { 8 | name = "dvh-controller" 9 | description = "A controller for dvh!" 10 | database { 11 | url = "env://BOUNDARY_PG_URL" 12 | } 13 | } 14 | 15 | worker { 16 | name = "dvh-worker" 17 | description = "A worker for a dvh project" 18 | 19 | tags { 20 | type = ["prod", "servers"] 21 | } 22 | 23 | public_addr = "192.168.1.15" 24 | } 25 | 26 | listener "tcp" { 27 | address = "boundary" 28 | purpose = "api" 29 | tls_disable = true 30 | #tls_cert_file = "/etc/boundary.d/tls/boundary-cert.pem" 31 | #tls_key_file = "/etc/boundary.d/tls/boundary-key.pem" 32 | } 33 | 34 | listener "tcp" { 35 | address = "boundary" 36 | purpose = "cluster" 37 | tls_disable = true 38 | #tls_cert_file = "/etc/boundary.d/tls/boundary-cert.pem" 39 | #tls_key_file = "/etc/boundary.d/tls/boundary-key.pem" 40 | } 41 | 42 | listener "tcp" { 43 | address = "boundary" 44 | purpose = "proxy" 45 | tls_disable = true 46 | #tls_cert_file = "/etc/boundary.d/tls/boundary-cert.pem" 47 | #tls_key_file = "/etc/boundary.d/tls/boundary-key.pem" 48 | } 49 | 50 | 51 | events { 52 | audit_enabled = false 53 | sysevents_enabled = true 54 | observations_enable = false 55 | 56 | sink "stderr" { 57 | name = "all-events" 58 | description = "All events sent to stderr" 59 | event_types = ["*"] 60 | format = "hclog-text" 61 | } 62 | sink { 63 | name = "audit-file" 64 | description = "audit events sent to a file" 65 | event_types = ["audit"] 66 | format = "cloudevents-json" 67 | 68 | file { 69 | path = "/var/log/boundary" 70 | file_name = "controller.log" 71 | } 72 | 73 | sink { 74 | name = "auth-sink" 75 | description = "Authentications sent to a file" 76 | event_types = ["observation"] 77 | format = "cloudevents-json" 78 | allow_filters = [ 79 | "\"/data/request_info/path\" contains \":authenticate\"" 80 | ] 81 | file { 82 | path = "/var/log/boundary" 83 | file_name = "auth.log" 84 | } 85 | } 86 | 87 | sink { 88 | name = "session-sink" 89 | description = "Authorize session requests and services sent to a file" 90 | event_types = ["audit"] 91 | format = "cloudevents-json" 92 | allow_filters = [ 93 | "\"/data/request_info/path\" contains \":authorize-session\"", 94 | "\"/data/request_info/method\" contains \"SessionService\"", 95 | ] 96 | file { 97 | path = "/var/log/boundary" 98 | file_name = "sessions.log" 99 | } 100 | } 101 | 102 | audit_config { 103 | audit_filter_overrides { 104 | secret = "encrypt" 105 | sensitive = "hmac-sha256" 106 | } 107 | } 108 | } 109 | } 110 | 111 | 112 | 113 | kms "transit" { 114 | purpose = "root" 115 | address = "http://vault:8200" 116 | // token = "hvs.qtHSKlalvkOgLZKGFRmHj90v" 117 | disable_renewal = "false" 118 | 119 | // Key configuration 120 | key_name = "root-boundary" 121 | mount_path = "transit/" 122 | tls_skip_verify = "true" 123 | vault_prefix = "transit/" 124 | } 125 | kms "transit" { 126 | purpose = "worker-auth" 127 | address = "http://vault:8200" 128 | // token = "hvs.qtHSKlalvkOgLZKGFRmHj90v" 129 | disable_renewal = "false" 130 | 131 | // Key configuration 132 | key_name = "worker-auth-boundary" 133 | mount_path = "transit/" 134 | tls_skip_verify = "true" 135 | vault_prefix = "transit/" 136 | } 137 | kms "transit" { 138 | purpose = "recovery" 139 | address = "http://vault:8200" 140 | // token = "hvs.qtHSKlalvkOgLZKGFRmHj90v" 141 | disable_renewal = "false" 142 | 143 | // Key configuration 144 | key_name = "recovery-boundary" 145 | mount_path = "transit/" 146 | tls_skip_verify = "true" 147 | vault_prefix = "transit/" 148 | } 149 | -------------------------------------------------------------------------------- /boundary/terraform/README.md: -------------------------------------------------------------------------------- 1 | # Hashicorp Boundary Provider 2 | This provider aims at creating and managing Hashicorp boundary resources using best practices. 3 | 4 | ## How To Use 5 | 6 | **First, make sure you have Boundary and Vault (transit key auth) up & running.** 7 | 1. Create a terraform.tfvars file and fill it in with optional values according to `variables.tf` 8 | 2. Run the conventional terraform commands to deploy IaC to your boundary instance 9 | ``` 10 | export BOUNDARY_ADDR="https://your-boundary-instance.com" 11 | export VAULT_TOKEN="vault-transit-token" 12 | 13 | terraform init 14 | terraform fmt 15 | terraform validate 16 | terraform plan 17 | terrafrom apply 18 | ``` 19 | 20 | **Replace `vault-transit-token` and `https://your-boundary-instance.com` with your actual values.** 21 | 22 | ## Bear In Mind 23 | **Using the `Vault Transit recovery key` for auth with Terraform is a best practice, you can create a password auth method manually ON BOUNDARY UI and then `auth_method` related lines in `provider.tf`; This way you won't necessarily need Vault to use this template.** 24 | 25 | - Breaking down resources in `main.tf` into separate tf files is also a plus point for better readability. 26 | 27 | - Some features like Credential Injection and Session recording solely come by the paid plan. so uncomment them as you wish in `main.tf`. 28 | 29 | - If you're utilizing Vault recovery key ensure `recovery_kms_hcl` directives in `provider.tf` to match your Vault transit engine credentials. -------------------------------------------------------------------------------- /boundary/terraform/main.tf: -------------------------------------------------------------------------------- 1 | # data sources 2 | data "boundary_auth_method" "auth_method" { 3 | name = "password" 4 | } 5 | 6 | data "boundary_user" "global_scope_admin" { 7 | name = "admin" 8 | } 9 | #---------- 10 | 11 | resource "boundary_scope" "global" { 12 | name = "Global" 13 | global_scope = true 14 | description = "highest-level Scope for administrators" 15 | scope_id = "global" 16 | } 17 | 18 | resource "boundary_scope" "corp" { 19 | name = "DVH" 20 | description = "DVH scope" 21 | scope_id = boundary_scope.global.id 22 | auto_create_admin_role = true 23 | auto_create_default_role = true 24 | } 25 | 26 | resource "boundary_scope" "core_infra" { 27 | name = "tf-templates" 28 | description = "DVH delivery project" 29 | scope_id = boundary_scope.corp.id 30 | auto_create_admin_role = true 31 | } 32 | 33 | ## Use password auth method 34 | resource "boundary_auth_method" "password" { 35 | name = "DVH Password" 36 | scope_id = boundary_scope.corp.id 37 | type = "password" 38 | } 39 | 40 | ## users and passwords 41 | resource "boundary_account_password" "client_acct" { 42 | for_each = var.clients 43 | name = each.key 44 | description = "User account for ${each.key}" 45 | # type = "password" 46 | login_name = lower(each.key) 47 | password = each.value 48 | auth_method_id = boundary_auth_method.password.id 49 | } 50 | resource "boundary_account_password" "admins_acct" { 51 | for_each = var.admins 52 | name = "${each.key} account" 53 | description = "User account for ${each.key}" 54 | # type = "password" 55 | login_name = lower(each.key) 56 | password = each.value 57 | auth_method_id = boundary_auth_method.password.id 58 | } 59 | 60 | resource "boundary_account_password" "readonly_users_acct" { 61 | for_each = var.readonly_users 62 | name = each.key 63 | description = "User account for ${each.key}" 64 | # type = "password" 65 | login_name = lower(each.key) 66 | password = each.value 67 | auth_method_id = boundary_auth_method.password.id 68 | } 69 | 70 | resource "boundary_user" "clients" { 71 | for_each = var.clients 72 | name = each.key 73 | description = "User resource for ${each.key}" 74 | scope_id = boundary_scope.corp.id 75 | account_ids = [boundary_account_password.client_acct[each.key].id] 76 | } 77 | resource "boundary_user" "admins" { 78 | for_each = var.admins 79 | name = each.key 80 | description = "User resource for ${each.key}" 81 | scope_id = boundary_scope.corp.id 82 | account_ids = [boundary_account_password.admins_acct[each.key].id] 83 | } 84 | ### readonly users 85 | resource "boundary_user" "readonly_users" { 86 | for_each = var.readonly_users 87 | name = each.key 88 | description = "User resource for ${each.key}" 89 | scope_id = boundary_scope.corp.id 90 | account_ids = [for account in boundary_account_password.readonly_users_acct : account.id] 91 | } 92 | 93 | ## groups and roles 94 | resource "boundary_group" "readonly_group" { 95 | name = "read-only" 96 | description = "Organization group for readonly users" 97 | member_ids = [for user in boundary_user.readonly_users : user.id] 98 | scope_id = boundary_scope.corp.id 99 | } 100 | resource "boundary_group" "clients_group" { 101 | name = "clients-group" 102 | description = "Organization group for regular users" 103 | member_ids = [for user in boundary_user.clients : user.id] 104 | scope_id = boundary_scope.corp.id 105 | } 106 | resource "boundary_group" "admin_group" { 107 | name = "admins-group" 108 | description = "Organization group for regular users" 109 | member_ids = [for user in boundary_user.admins : user.id] 110 | scope_id = boundary_scope.corp.id 111 | } 112 | 113 | resource "boundary_role" "dvh_readonly" { 114 | name = "Read-only" 115 | description = "Read-only role" 116 | principal_ids = [boundary_group.readonly_group.id] 117 | grant_strings = [var.all_list] 118 | scope_id = boundary_scope.core_infra.id 119 | } 120 | 121 | resource "boundary_role" "dvh_admin" { 122 | name = "admin" 123 | description = "Administrator role" 124 | principal_ids = [boundary_group.admin_group.id] 125 | grant_strings = [var.all_actions] 126 | scope_id = boundary_scope.core_infra.id 127 | } 128 | 129 | resource "boundary_role" "dvh_client" { 130 | name = "client" 131 | description = "client role for mereley connecting through boudary." 132 | principal_ids = [boundary_group.clients_group.id] 133 | grant_strings = [var.all_list, var.target_authorize_session, var.auth_method_authenticate] 134 | scope_id = boundary_scope.core_infra.id 135 | } 136 | 137 | 138 | ## Main servers Targets configuration 139 | resource "boundary_host_catalog_static" "main_servers" { 140 | name = "main_servers" 141 | description = "main servers host catalog" 142 | scope_id = boundary_scope.core_infra.id 143 | } 144 | 145 | resource "boundary_host_static" "main_servers" { 146 | for_each = { for host in var.hosts_info : host.name => host } 147 | type = "static" 148 | name = each.value.name 149 | description = "Main server host" 150 | address = each.value.ip 151 | host_catalog_id = boundary_host_catalog_static.main_servers.id 152 | } 153 | 154 | resource "boundary_host_set_static" "main_servers_ssh" { 155 | type = "static" 156 | name = "main_servers_ssh" 157 | description = "Host set for main servers" 158 | host_catalog_id = boundary_host_catalog_static.main_servers.id 159 | host_ids = [for host in boundary_host_static.main_servers : host.id] 160 | } 161 | 162 | 163 | ## credential stores 164 | resource "boundary_credential_store_static" "main_cred_store" { 165 | name = var.main_cred_store_name 166 | scope_id = boundary_scope.core_infra.id 167 | } 168 | 169 | resource "boundary_credential_ssh_private_key" "main_server_keys" { 170 | for_each = { for host in var.hosts_info : host.name => host } 171 | name = each.value.ssh_key_name 172 | username = sensitive(each.value.ssh_user) 173 | credential_store_id = boundary_credential_store_static.main_cred_store.id 174 | private_key = file(each.value.ssh_key_path) 175 | } 176 | 177 | # ssh target for DVH main servers 178 | resource "boundary_target" "main_servers_ssh" { 179 | type = "tcp" 180 | for_each = { for host in var.hosts_info : host.name => host } 181 | name = "${each.value.name}_server" 182 | description = "Main servers SSH target" 183 | scope_id = boundary_scope.core_infra.id 184 | brokered_credential_source_ids = [for credential in boundary_credential_ssh_private_key.main_server_keys : credential.id] 185 | default_port = each.value.ssh_port 186 | 187 | host_source_ids = [ 188 | boundary_host_set_static.main_servers_ssh.id 189 | ] 190 | } 191 | 192 | #### Start of Vault ssh credential store 193 | # The following resources are only applied if SSH_INJECTION is set to True. 194 | 195 | resource "boundary_credential_store_vault" "vault_cert_store" { 196 | count = var.SSH_INJECTION ? 1 : 0 197 | name = "vault-cred-store" 198 | address = var.vault_address 199 | token = var.vault_cred_store_token 200 | scope_id = boundary_scope.core_infra.id 201 | } 202 | 203 | resource "boundary_credential_library_vault_ssh_certificate" "vault_cred_lib_ssh" { 204 | count = var.SSH_INJECTION ? 1 : 0 205 | name = "certificates-library" 206 | credential_store_id = boundary_credential_store_vault.vault_cert_store[count.index].id 207 | path = var.vault_sign_path 208 | username = var.vault_username 209 | key_type = "ecdsa" 210 | key_bits = 521 211 | } 212 | 213 | resource "boundary_target" "test_server_ssh" { 214 | count = var.SSH_INJECTION ? 1 : 0 215 | type = "tcp" 216 | name = "${var.test_server_name}_ssh_server" 217 | description = "test servers SSH target" 218 | scope_id = boundary_scope.core_infra.id 219 | default_port = var.test_ssh_port 220 | injected_application_credential_source_ids = [boundary_credential_library_vault_ssh_certificate.vault_cred_lib_ssh[count.index].id] 221 | host_source_ids = [ 222 | boundary_host_set_static.main_servers_ssh.id 223 | ] 224 | } 225 | ### End of cred injection 226 | 227 | # TODO: break resources into seperate tf files -------------------------------------------------------------------------------- /boundary/terraform/output.tf: -------------------------------------------------------------------------------- 1 | output "ssh_key_ids" { 2 | value = "IDs for ssh_private keys: ${join(", ", values(boundary_credential_ssh_private_key.main_server_keys)[*].id)}" 3 | description = "show the id of ssh private key" 4 | } 5 | 6 | output "auth_method_id" { 7 | value = "BOUNDARY_AUTH_METHOD_ID: ${data.boundary_auth_method.auth_method.id}" 8 | description = "BOUNDARY_AUTH_METHOD_ID value!" 9 | } 10 | 11 | output "target_id" { 12 | value = "main servers TARGET_ID for staging, production: ${join(", ", values(boundary_target.main_servers_ssh)[*].id)}" 13 | description = "show the id of ssh private key" 14 | } 15 | 16 | output "scope_id" { 17 | value = "the scope id for admins: ${boundary_scope.corp.scope_id}" 18 | description = "show the id of ssh private key" 19 | } 20 | 21 | output "boundary_address" { 22 | value = "BOUNDARY_ADDR : ${var.boundary_address}" 23 | description = "the address that boundary is serving at!" 24 | sensitive = true 25 | } 26 | 27 | 28 | output "user_ids" { 29 | value = "admin user_ids is ${data.boundary_user.global_scope_admin.id} and admin username is ${data.boundary_user.global_scope_admin.name}" 30 | } 31 | -------------------------------------------------------------------------------- /boundary/terraform/provider.tf: -------------------------------------------------------------------------------- 1 | provider "boundary" { 2 | addr = var.boundary_address 3 | # auth_method_id = var.auth_method_id 4 | # auth_method_login_name = var.login_name 5 | # auth_method_password = var.login_password 6 | 7 | ## Address is the VAULT SERVER ADDRESS 8 | recovery_kms_hcl = <> $tfvars_file 19 | return 0 20 | } 21 | 22 | function delete_token(){ 23 | sed -i '/^transit_token/d' $var_file 24 | } 25 | 26 | if [[ $1 == "-d" ]]; then 27 | delete_token 28 | elif [[ $1 == "ssh" ]];then 29 | inject_ssh_cred 30 | fi 31 | 32 | if grep "transit_token" $var_file &> /dev/null; then 33 | delete_token 34 | fi 35 | echo -e "\ntransit_token: $token" >> $var_file -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "${STACK_DIR}/scripts/linter.sh" 4 | 5 | export COMPOSE_DIR="${STACK_DIR}/deploy" 6 | 7 | function boundary() { 8 | 9 | export VAULT_TOKEN=${VAULT_TOKEN} 10 | export VAULT_ADDR=${VAULT_ADDR} 11 | 12 | lint_docker 13 | 14 | if [[ "$STACK_INIT" == "false" ]]; then 15 | docker compose -f "${COMPOSE_DIR}/boundary.yml" up -d postgres_db boundary wait 16 | return $? 17 | fi 18 | docker compose -f "${COMPOSE_DIR}/boundary.yml" up -d 19 | 20 | return 0 21 | } 22 | 23 | function vault() { 24 | 25 | lint_docker 26 | 27 | if [[ "$STACK_INIT" == "false" ]]; then 28 | docker compose -f "${COMPOSE_DIR}/vault.yml" up -d vault 29 | return $? 30 | fi 31 | 32 | docker compose -f "${COMPOSE_DIR}/vault.yml" up -d 33 | 34 | return 0 35 | } 36 | 37 | 38 | if [[ "$1" == "boundary" ]]; then 39 | boundary 40 | elif [[ "$1" == "vault" ]]; then 41 | vault 42 | else 43 | echo "ERROR : Pleasae provide the service name you want to deploy" >&2 44 | exit 4 45 | fi -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | # !/bin/env bash 2 | 3 | set -e -o pipefail 4 | 5 | function usage() { 6 | cat < 9 | 10 | Description: 11 | This script inits boundary/vault setup/IaC (terraform). 12 | 13 | Options: 14 | -h, --help Display this help message and exit 15 | 16 | Arguments: 17 | boundary Apply terrform configurations for boundary 18 | vault Apply terrform configurations for vault 19 | path/to/basedir Choose a base dir for the project (optional) 20 | 21 | Example: 22 | 23 | - init vault: 24 | $(basename "$0") vault /PATH/TO/BASEDIR 25 | 26 | - init boundary: 27 | $(basename "$0") boundary /PATH/TO/BASEDIR 28 | 29 | 30 | EOF 31 | } 32 | 33 | if [[ $# -eq 0 ]]; then 34 | usage 35 | exit 0 36 | fi 37 | 38 | # source into linter script 39 | if [[ $1 != "vault-init" ]]; then 40 | source "${HOME_DIR}/scripts/linter.sh" 41 | fi 42 | 43 | STACK_DIR="${2:-/home/ubuntu/boundary-vault-stack}" 44 | 45 | # this function is for dev env 46 | function init_compose(){ 47 | cd compose/ 48 | 49 | lint_docker 50 | 51 | docker-compose -f docker-compose.yml up -d 52 | } 53 | 54 | function init_boundary_iac(){ 55 | export HOME_DIR="${HOME_DIR}" 56 | 57 | cd "${HOME_DIR}/boundary/terraform/" 58 | lint_terraform 59 | 60 | secret_file="${HOME_DIR}/secrets/secrets.txt" 61 | token=$(cat $secret_file | grep "transit-token" | awk '{print $2}') 62 | export VAULT_TOKEN="$token" 63 | export BOUNDARY_ADDR="$BOUNDARY_ADDR" 64 | # ssh_injection=$(echo "$SSH_INJECTION" | tr '[:upper:]' '[:lower:]') 65 | # export TF_VAR_SSH_INJECTION=$ssh_injection 66 | 67 | log_path="${HOME_DIR}/logs/terraform/boundary-logs.txt" 68 | if ! terraform plan &> /dev/null; then 69 | echo -e "Terraform Plan failed for Vault, check the logs at $log_path \n\n" >&2 70 | fi 71 | terraform apply --auto-approve 2>&1 | sed -r "s/\x1B\[[0-9;]*[mGKH]//g" > $log_path 72 | } 73 | 74 | function init_vault_iac(){ 75 | export HOME_DIR="${HOME_DIR}" 76 | 77 | cd "${HOME_DIR}/vault/terraform" 78 | lint_terraform 79 | 80 | secret_file="${HOME_DIR}/secrets/secrets.txt" 81 | root_token="$(cat $secret_file | head -n1 | awk '{print $8}')" 82 | export VAULT_TOKEN="$root_token" 83 | 84 | log_path="${HOME_DIR}/logs/terraform/vault-logs.txt" 85 | if ! terraform plan &> /dev/null ;then 86 | echo -e "Terraform Plan failed for Vault, check the logs at $log_path \n\n" >&2 87 | fi 88 | terraform apply --auto-approve 2>&1 | sed -r "s/\x1B\[[0-9;]*[mGKH]//g" > $log_path 89 | } 90 | 91 | ## vault init container to setup vault and get killed right after. 92 | function init_vault_setup(){ 93 | secret_file="/home/secrets.txt" 94 | 95 | export VAULT_ADDR='http://vault:8200' 96 | 97 | ### initializing and unsealing vault 98 | init_secrets="$(vault operator init -key-shares=1 -key-threshold=1)" 99 | 100 | unseal_key="$(echo $init_secrets | grep -i Unseal | awk '{print $4}')" 101 | root_token="$(echo $init_secrets | awk '{print $8}')" 102 | 103 | export UNSEAL_KEY="$unseal_key" 104 | 105 | echo $init_secrets > $secret_file 106 | 107 | vault operator unseal $UNSEAL_KEY 108 | 109 | echo -n "$root_token" | vault login - 110 | 111 | vault audit enable file file_path="/vault/logs/vault_audit.log" 112 | vault write sys/quotas/config enable_rate_limit_audit_logging=true 113 | 114 | 115 | ### add transit engine and apply rate-limit 116 | # vault write sys/quotas/rate-limit/transit-limit \ 117 | # path="transit" \ 118 | # rate=10 \ 119 | # interval=60 120 | 121 | ### configure boundary ssh brokering 122 | # enable ssh secret engine 123 | vault secrets enable -path=ssh-signer ssh 124 | 125 | export CRED_STORE_TOKEN=$(vault token create \ 126 | -no-default-policy=true \ 127 | -policy="boundary-controller" \ 128 | -policy="ssh" \ 129 | -orphan=true \ 130 | -period=24h \ 131 | -renewable=true | grep "hvs" | awk '{print $2}') 132 | 133 | vault write --format=json ssh-signer/config/ca generate_signing_key=true | grep -o '"public_key":.*' | awk -F'"' '{print $4}' \ 134 | > /home/ca-key.pub 135 | 136 | transit_token=$(vault token create \ 137 | -policy="boundary-transit" \ 138 | | grep "hvs" | awk '{print $2}') 139 | echo -e "\ntransit-token $transit_token \nvault_cred_store_token \"${CRED_STORE_TOKEN}\"" >> $secret_file 140 | } 141 | 142 | export VAULT_ADDR="$VAULT_ADDR" 143 | 144 | if [[ $1 == "vault" ]]; then 145 | 146 | init_vault_iac 147 | 148 | exit 0 149 | fi 150 | 151 | if [[ $1 == "boundary" ]]; then 152 | 153 | init_boundary_iac 154 | exit 0 155 | fi 156 | 157 | if [[ $1 == "vault-init" ]]; then 158 | init_vault_setup 159 | exit 0 160 | fi 161 | 162 | if [[ "$1" == "-h" || $1 == "--help" ]]; then 163 | usage 164 | exit 0 165 | fi 166 | 167 | 168 | -------------------------------------------------------------------------------- /scripts/install_services.sh: -------------------------------------------------------------------------------- 1 | 2 | if ! command -v docker &> /dev/null; then 3 | sudo apt-get update 4 | sudo apt install ca-certificates curl 5 | sudo install -m 0755 -d /etc/apt/keyrings 6 | sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc 7 | sudo chmod a+r /etc/apt/keyrings/docker.asc 8 | 9 | # Add the repository to Apt sources: 10 | echo \ 11 | "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ 12 | $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ 13 | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 14 | 15 | sudo apt update 16 | 17 | sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-compose 18 | 19 | sudo usermod -aG docker vagrant 20 | 21 | newgrp docker 22 | 23 | fi 24 | 25 | if ! command -v terraform &> /dev/null; then 26 | 27 | sudo apt-get update && sudo apt-get install -y gnupg software-properties-common 28 | wget -O- https://apt.releases.hashicorp.com/gpg | \ 29 | gpg --dearmor | \ 30 | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null 31 | 32 | gpg --no-default-keyring \ 33 | --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \ 34 | --fingerprint 35 | echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \ 36 | https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \ 37 | sudo tee /etc/apt/sources.list.d/hashicorp.list 38 | sudo apt update 39 | sudo apt-get install terraform 40 | fi 41 | exit 0 -------------------------------------------------------------------------------- /scripts/install_vagrant.sh: -------------------------------------------------------------------------------- 1 | wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg && 2 | echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list && 3 | sudo apt-get update && sudo apt install vagrant -------------------------------------------------------------------------------- /scripts/install_virtual_box.sh: -------------------------------------------------------------------------------- 1 | sudo apt-get install virtualbox -------------------------------------------------------------------------------- /scripts/linter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | function lint_terraform(){ 6 | 7 | if ! command -v terraform &> /dev/null; then 8 | echo -e "ERROR: Terraform is not installed!" >&2 9 | echo -e "Please install Terraform from https://www.terraform.io/downloads.html" >&2 10 | return 1 11 | fi 12 | 13 | if ! terraform init &> /dev/null; then 14 | echo "ERROR: Terraform init failed!" >&2 15 | echo "Ensure you are in the correct directory and check the Terraform configuration." >&2 16 | return 2 17 | fi 18 | 19 | if ! terraform validate &> /dev/null; then 20 | echo "ERROR: Terraform configuration is not valid!" >&2 21 | echo "Check the output above for details on what went wrong." >&2 22 | return 3 23 | fi 24 | 25 | return 0 26 | } 27 | 28 | function lint_docker () { 29 | 30 | if ! command -v docker &> /dev/null; then 31 | echo -e "ERROR: Docker is not installed!" >&2 32 | echo -e "Please install Docker from https://docs.docker.com/get-docker/" >&2 33 | return 1 34 | fi 35 | 36 | if ! docker compose version &> /dev/null; then 37 | echo "WARNING: Docker Version is not 2+" 38 | fi 39 | 40 | if ! docker-compose --version &> /dev/null; then 41 | echo "ERROR: Docker Compose is not installed!" >&2 42 | echo "Please install Docker Compose from https://docs.docker.com/compose/install/" >&2 43 | return 1 44 | fi 45 | 46 | return 0 47 | 48 | } 49 | 50 | function lint_vagrant(){ 51 | if ! command -v vagrant &> /dev/null; then 52 | echo -e "ERROR: Vagrant is not installed!" >&2 53 | echo -e "Please install Vagrant from https://developer.hashicorp.com/vagrant/downloads" >&2 54 | return 1 55 | fi 56 | 57 | if ! command -v VBoxManage &> /dev/null; then 58 | echo -e "ERROR: VirtualBox is not installed \nVagrant uses Virtualbox to provision vms." >&2 59 | echo -e "Please install VirtualBox from https://virtualbox.org/wiki/Linux_Downloads" >&2 60 | return 1 61 | fi 62 | return 0 63 | } 64 | 65 | function lint_py(){ 66 | if ! command -v python3 && ! command -v python ; then 67 | echo "Error: Python Is Not Installed." >&2 68 | return 1 69 | fi 70 | return 0 71 | } 72 | 73 | function lint_ansible () { 74 | 75 | cd ../ansible || { echo "Failed to change directory to ansible"; return 1; } 76 | 77 | playbooks=$(find . -maxdepth 1 -name "*.yml" -print) 78 | for play in $playbooks; do 79 | if ! ansible-playbook $play --syntax-check &> /dev/null; then 80 | echo "Ansible Syntax Error: syntax check failed for $play, check the underlying roles!" 81 | return 3 82 | fi 83 | echo "$play is fine in terms of syntax!" 84 | done 85 | return 0 86 | } 87 | 88 | if [[ $1 == "ansible" ]]; then 89 | lint_ansible 90 | fi -------------------------------------------------------------------------------- /scripts/pull-images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -o pipefail 3 | 4 | GREEN='\033[0;32m' 5 | RED='\033[0;31m' 6 | RESET='\033[0m' 7 | 8 | images=("hashicorp/vault:1.13.3" "bitnami/postgresql:16" "hashicorp/boundary:0.14" "busybox:latest") 9 | 10 | for image_name in "${images[@]}"; do 11 | docker pull $image_name &> /dev/null 12 | if [ $? -ne 0 ]; then 13 | echo -e "${RED}ERROR : failed to pull image : $image_name${RESET}" >&2 14 | exit 1 15 | fi 16 | echo -e "${GREEN}downloaded $image_name ${RESET}\n" >&2 17 | done 18 | echo -e "All images got pulled successfully" 19 | exit 0 20 | -------------------------------------------------------------------------------- /scripts/tls-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/env sh 2 | 3 | set -e 4 | STACK_DIR="/home/ubuntu/boundary-vault-stack" 5 | key_dir="${STACK_DIR}/keys" 6 | 7 | service="boundary" 8 | 9 | openssl genpkey -algorithm RSA -out "${key_dir}/${service}/${service}-key.pem" -aes256 10 | 11 | openssl req -new -key "${key_dir}/${service}/${service}-key.pem "-out "${key_dir}/${service}/${service}-csr.pem" 12 | 13 | openssl x509 -req -days 365 -in "${key_dir}/${service}/${service}-csr.pem" -signkey "${key_dir}/$service/${service}-key.pem" -out "${key_dir}/${service}/${service}-cert.pem" 14 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | function usage(){ 6 | 7 | cat < 10 | 11 | Description: 12 | This script initiates boundary vault stack in your desired environment. 13 | 14 | Options: 15 | --help Display this help message and exit 16 | -e Define which environment to run the stack in (development/staging/test/production). 17 | 18 | Example: 19 | 20 | - run in dev: 21 | $(basename "$0") -e development 22 | 23 | EOF 24 | } 25 | 26 | if [[ $1 == "--help" ]]; then 27 | usage 28 | exit 0 29 | fi 30 | 31 | function check_env(){ 32 | case "$1" in 33 | development | staging | test | production) 34 | return 0 35 | ;; 36 | *) 37 | echo "ERROR: Invalid value for -e : ${1} , For more info use --help or reach out to Documentation." >&2 38 | return 4 39 | ;; 40 | esac 41 | } 42 | 43 | if [ -z "$STACK_ENV" ]; then 44 | while getopts "e:" opt; do 45 | case $opt in 46 | e) 47 | check_env "$OPTARG" 48 | export STACK_ENV="${OPTARG}" 49 | echo "Running Boundary Vault Stack on ${STACK_ENV} Mode." 50 | ;; 51 | \?) 52 | echo "ERROR: Invalid option: -${OPTARG} For more info use --help or reach out to Documentation" >&2 53 | return 4 54 | ;; 55 | esac 56 | done 57 | elif ! check_env "$STACK_ENV"; then 58 | exit 4 59 | fi 60 | 61 | if [ $# -ne 2 ]; then 62 | usage 63 | exit 4 64 | fi 65 | 66 | 67 | echo -e "***Running Boundary Vault Stack on ${STACK_ENV} Mode.****\n" 68 | 69 | 70 | ## create ignored dirs in git for confidential data 71 | mkdir -p logs/ logs/docker logs/terraform secrets/ 72 | 73 | source ./scripts/linter.sh 74 | if [[ ! -d "venv/" ]]; then 75 | echo -e "\nInstalling Virtual Env and dependencies." 76 | 77 | py_cmd=$(lint_py) 78 | $py_cmd -m venv venv 79 | source venv/bin/activate 80 | pip install -U pip 81 | pip install -r ./requirements.txt 82 | else 83 | source venv/bin/activate 84 | pip install -r ./requirements.txt 85 | fi 86 | 87 | ## install required collections 88 | #!/bin/bash 89 | 90 | # Check if Vagrant is installed 91 | if command -v vagrant &> /dev/null 92 | then 93 | echo "Vagrant is installed" 94 | vagrant --version 95 | else 96 | sh scripts/install_vagrant.sh 97 | fi 98 | #install virtualbox 99 | sh scripts/install_virtual_box.sh 100 | 101 | ansible-galaxy collection install -r requirements.yml 102 | 103 | # provision the server 104 | if [ -z "$STACK_SERVER"]; then 105 | lint_vagrant 106 | vagrant up 107 | fi 108 | 109 | ansible-playbook -i ansible/inventory/inventory.ini ansible/playbook.yml --ask-vault-pass 110 | echo "****** Applying Vault changes ******" 111 | sleep 10 112 | ansible-playbook -i ansible/inventory/inventory.ini ansible/terraform.yml --ask-vault-pass 113 | echo "********* Applying terraform provisioning ******* " 114 | sleep 5 115 | ansible-playbook -i ansible/inventory/inventory.ini ansible/boundary.yml --ask-vault-pass 116 | 117 | echo "***** Performing Stack Cleanup *******" 118 | ansible-playbook -i ansible/inventory/inventory.ini ansible/cleanup.yml --ask-vault-pass -------------------------------------------------------------------------------- /vagrantfile: -------------------------------------------------------------------------------- 1 | # Vagrantfile for deploying a VM to VirtualBox with specified configurations 2 | 3 | Vagrant.configure("2") do |config| 4 | # Define the base box 5 | config.vm.box = "ubuntu/focal64" # or another box of your choice 6 | 7 | # Set the name of the VM 8 | config.vm.define "bvstack" do |node| 9 | node.vm.host_name = "bvstack" 10 | 11 | # Network configuration: Bridge 12 | node.vm.network "public_network", ip: "192.168.1.15" 13 | 14 | # Resources: CPU and RAM 15 | node.vm.provider "virtualbox" do |vb| 16 | vb.name = "bvstack" 17 | vb.memory = 4096 18 | vb.cpus = 2 19 | end 20 | end 21 | 22 | config.vm.provision "file", source: "~/.ssh/id_rsa.pub" , destination:"~/.ssh/id_rsa.pub" 23 | end 24 | -------------------------------------------------------------------------------- /vault/config/vault.hcl: -------------------------------------------------------------------------------- 1 | listener "tcp" { 2 | address = "0.0.0.0:8200" 3 | tls_disable = true 4 | #tls_cert_file = "/path/to/fullchain.pem" 5 | #tls_key_file = "/path/to/privkey.pem" 6 | } 7 | 8 | storage "raft" { 9 | path = "/vault/data" 10 | # maximum record/entry vault accepts from clients 11 | max_entry_size = 1048576 12 | node_id = "node1" 13 | } 14 | 15 | # login limit for users 16 | 17 | user_lockout "userpass" { 18 | lockout_threshold = "3" 19 | lockout_duration = "10m" 20 | } 21 | 22 | api_addr = "http://localhost:8200" 23 | cluster_addr = "http://127.0.0.1:8201" 24 | ui = true -------------------------------------------------------------------------------- /vault/policy/kv-policy.hcl: -------------------------------------------------------------------------------- 1 | # vault initial policy file 2 | 3 | path "kee/*" { 4 | capabilities = ["read"] 5 | } 6 | 7 | path "kv/*" { 8 | capabilities = ["read"] 9 | } -------------------------------------------------------------------------------- /vault/terraform/auth.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------- 2 | # Enable userpass auth method 3 | #-------------------------------- 4 | 5 | resource "vault_auth_backend" "userpass" { 6 | type = "userpass" 7 | path = var.userpass_path 8 | } 9 | 10 | # Create Admin users 11 | resource "vault_generic_endpoint" "admins" { 12 | for_each = var.login_creds 13 | depends_on = [vault_auth_backend.userpass] 14 | path = "auth/${var.userpass_path}/users/${each.key}" 15 | ignore_absent_fields = true 16 | 17 | data_json = <