├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── aws-kms-xks-proxy-issue.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── makefile.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── appspec.yml ├── buildspec.yml ├── codepipeline ├── README.md └── scripts │ ├── after-install │ ├── application-start │ ├── before-install │ └── validate-service ├── docker └── README.md ├── rpmspec └── xks-proxy.spec ├── scripts └── xks-proxy_cleanlogs ├── systemd ├── xks-proxy.service ├── xks-proxy_cleanlogs.service └── xks-proxy_cleanlogs.timer └── xks-axum ├── Cargo.toml ├── configuration ├── bootstrap.toml ├── oso.polar ├── settings.toml ├── settings_cloudhsm.toml ├── settings_docker.toml ├── settings_luna.toml ├── settings_nshield.toml ├── settings_softhsmv2.toml └── settings_softhsmv2_osx.toml ├── osx └── README.md ├── rust-pkcs11 ├── Cargo.toml ├── LICENSE ├── NOTICE ├── README.md ├── pkcs11-docs │ └── README.md └── src │ ├── errors.rs │ ├── functions.rs │ ├── lib.rs │ ├── tests.rs │ └── types.rs ├── src ├── main.rs ├── settings.rs ├── tls.rs └── xks_proxy │ ├── handlers │ ├── decrypt.rs │ ├── encrypt.rs │ ├── get_health_status.rs │ ├── get_key_meta_data.rs │ ├── mod.rs │ ├── oso_auth.rs │ └── testings │ │ ├── encrypt_test.rs │ │ └── mod.rs │ ├── mod.rs │ ├── pkcs11.rs │ └── sigv4.rs └── tls ├── client_ca.pem ├── server_cert.pem └── server_key.pem /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Each line is a file pattern followed by one or more owners. 2 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 3 | 4 | # Default code owner for everything in our aws-kms-xks group 5 | * @aws-samples/aws-kms-xks 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/aws-kms-xks-proxy-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: aws-kms-xks-proxy issue 3 | about: aws-kms-xks-proxy issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Security issue notifications 11 | 12 | If you discover a potential security issue in aws-kms-xks-proxy we ask that you notify 13 | AWS Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 14 | 15 | ### Problem: 16 | 17 | A short description of what the problem is and why we need to fix it. Add reproduction steps if necessary. 18 | 19 | ### Solution: 20 | 21 | A description of the possible solution in terms of aws-kms-xks-proxy architecture. 22 | 23 | ### Out of scope: 24 | 25 | Is there anything the solution will intentionally NOT address? 26 | 27 | [//]: # (NOTE: If you believe this might be a security issue, please email aws-security@amazon.com instead of creating a GitHub issue. For more details, see the AWS Vulnerability Reporting Guide: https://aws.amazon.com/security/vulnerability-reporting/ ) 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | 8 | # Check any applicable: 9 | - [ ] Were any files moved? Moving files changes their URL, which breaks all hyperlinks to the files. 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/makefile.yml: -------------------------------------------------------------------------------- 1 | name: Makefile CI 2 | 3 | env: 4 | RUST_BACKTRACE: 1 5 | PROJECT_DIR: xks-axum 6 | 7 | on: 8 | pull_request: 9 | push: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | style: 14 | name: Check Style 15 | runs-on: ubuntu-22.04 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Install Rust 21 | uses: dtolnay/rust-toolchain@stable 22 | with: 23 | components: rustfmt 24 | 25 | - name: Check format 26 | run: cargo fmt --manifest-path=${{ env.PROJECT_DIR }}/Cargo.toml --all -- --check 27 | 28 | test-n-clippy: 29 | name: Unit Test and Clippy 30 | strategy: 31 | matrix: 32 | os: [ubuntu-22.04, al2, al2aarch64] 33 | runs-on: ${{ matrix.os }} 34 | needs: style 35 | 36 | steps: 37 | - uses: actions/checkout@v3 38 | 39 | - name: Install Rust Stable 40 | uses: dtolnay/rust-toolchain@stable 41 | with: 42 | components: clippy 43 | 44 | - name: Unit Test 45 | run: cargo test --manifest-path=${{ env.PROJECT_DIR }}/Cargo.toml 46 | 47 | - name: Clippy 48 | run: cargo clippy --manifest-path=${{ env.PROJECT_DIR }}/Cargo.toml 49 | 50 | miri: 51 | name: Check for Undefined Behavior 52 | strategy: 53 | matrix: 54 | os: [ubuntu-22.04, al2, al2aarch64] 55 | runs-on: ${{ matrix.os }} 56 | needs: style 57 | 58 | steps: 59 | - uses: actions/checkout@v3 60 | 61 | - name: Install Rust Nightly 62 | uses: dtolnay/rust-toolchain@nightly 63 | with: 64 | components: miri 65 | 66 | - name: Miri Test 67 | env: 68 | MIRIFLAGS: -Zmiri-disable-isolation 69 | run: cargo +nightly miri test --manifest-path=${{ env.PROJECT_DIR }}/Cargo.toml 70 | 71 | check-on-macos: 72 | name: Check on Mac 73 | runs-on: macos-12 74 | needs: style 75 | 76 | steps: 77 | - uses: actions/checkout@v3 78 | 79 | - name: Install Rust 80 | uses: dtolnay/rust-toolchain@stable 81 | 82 | - name: Check 83 | run: cargo check --manifest-path=${{ env.PROJECT_DIR }}/Cargo.toml 84 | 85 | # The pkcs11 crate v0.5.0 doesn't compile on Windows 86 | # https://docs.rs/pkcs11/latest/pkcs11/ 87 | # check-on-windows: 88 | # runs-on: windows-latest 89 | 90 | build: 91 | name: Build Packages 92 | strategy: 93 | matrix: 94 | config: 95 | - { os: ubuntu-22.04, arch: x86_64 } 96 | - { os: al2, arch: x86_64 } 97 | - { os: al2aarch64, arch: aarch64 } 98 | runs-on: ${{ matrix.config.os }} 99 | 100 | needs: [test-n-clippy, miri, check-on-macos] 101 | 102 | steps: 103 | - uses: actions/checkout@v3 104 | 105 | - name: Install Rust Stable 106 | uses: dtolnay/rust-toolchain@stable 107 | 108 | - name: Install other dependencies 109 | run: | 110 | if type apt-get 2>/dev/null; then 111 | # Install on Debian/Ubuntu 112 | sudo apt-get install -y rpm alien 113 | fi 114 | if type yum 2>/dev/null; then 115 | # Install on Centos/AL2 116 | sudo yum install -y rpmdevtools rpm-build rpm-devel rpmlint 117 | fi 118 | 119 | - name: Build xks-proxy rpm and deb 120 | run: | 121 | make 122 | rpm_name=$(basename ~/rpmbuild/RPMS/**/*.rpm) 123 | echo "RPM_NAME=$rpm_name" >> $GITHUB_ENV 124 | if ls *.deb >/dev/null 2>&1; then 125 | deb_name=$(ls *.deb) 126 | # Include the architecture as part of the file name 127 | arch_deb_name=${deb_name/.deb/-${{ matrix.config.arch }}.deb} 128 | mv "$deb_name" "$arch_deb_name" 129 | echo "DEB_ARTIFACT=$arch_deb_name" >> $GITHUB_ENV 130 | else 131 | echo "DEB_ARTIFACT=None" >> $GITHUB_ENV 132 | fi 133 | 134 | - name: Upload rpm 135 | uses: actions/upload-artifact@v3 136 | with: 137 | name: ${{ env.RPM_NAME }} 138 | path: ~/rpmbuild/RPMS/**/${{ env.RPM_NAME }} 139 | 140 | - name: Upload deb 141 | if: env.DEB_ARTIFACT != 'None' 142 | uses: actions/upload-artifact@v3 143 | with: 144 | name: ${{ env.DEB_ARTIFACT }} 145 | path: ./*.deb 146 | 147 | build-docker: 148 | name: Build Docker Image 149 | strategy: 150 | matrix: 151 | # https://github.com/orgs/community/discussions/25949 152 | config: 153 | - { os: al2, arch: x86_64 } 154 | - { os: al2aarch64, arch: aarch64 } 155 | runs-on: ${{ matrix.config.os }} 156 | needs: [test-n-clippy, miri, check-on-macos] 157 | 158 | steps: 159 | - uses: actions/checkout@v3 160 | 161 | - name: Install Docker 162 | run: | 163 | if type yum 2>/dev/null; then 164 | # Install on Centos/AL2 165 | sudo yum install -y docker 166 | fi 167 | 168 | # https://stackoverflow.com/questions/53918841/how-to-install-docker-on-amazon-linux2 169 | - name: Start Docker 170 | run: | 171 | if type yum 2>/dev/null; then 172 | sudo service docker start 173 | sudo usermod -a -G docker ec2-user 174 | sudo chmod 666 /var/run/docker.sock 175 | docker version 176 | fi 177 | 178 | - name: Build docker image 179 | run: | 180 | docker build -t xks-proxy:latest . 181 | docker save -o xks-proxy-docker-v3.1.2-${{ matrix.config.arch }}.tar xks-proxy:latest 182 | xz -z -0 xks-proxy-docker-v3.1.2-${{ matrix.config.arch }}.tar 183 | 184 | - name: Upload docker image 185 | uses: actions/upload-artifact@v3 186 | with: 187 | name: xks-proxy-docker-v3.1.2-${{ matrix.config.arch }}.tar.xz 188 | path: ./xks-proxy-docker-v3.1.2-${{ matrix.config.arch }}.tar.xz 189 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | pkcs11-logger-x64.dylib 5 | install_rpm_tools 6 | xks-axum/Cargo.lock 7 | xks-axum/target 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # See docker/README.md for more information. 5 | 6 | FROM ubuntu as builder 7 | 8 | ENV HOME=/root 9 | RUN mkdir -p $HOME/aws-kms-xks-proxy 10 | COPY ./xks-axum $HOME/aws-kms-xks-proxy/xks-axum 11 | 12 | RUN apt-get update -y 13 | RUN apt-get install -y softhsm2 opensc curl build-essential 14 | 15 | RUN softhsm2-util --init-token --slot 0 --label "xks-proxy" --so-pin 1234 --pin 1234 16 | RUN pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \ 17 | --token-label xks-proxy --login --login-type user \ 18 | --keygen --id F0 --label foo --key-type aes:32 \ 19 | --pin 1234 20 | 21 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 22 | ENV PATH="$HOME/.cargo/bin:$PATH" 23 | 24 | RUN mkdir -p /var/local/xks-proxy/.secret 25 | COPY ./xks-axum/configuration/settings_docker.toml /var/local/xks-proxy/.secret/settings.toml 26 | 27 | ENV PROJECT_DIR=$HOME/aws-kms-xks-proxy/xks-axum 28 | RUN cargo build --release --manifest-path=$PROJECT_DIR/Cargo.toml && \ 29 | cp $PROJECT_DIR/target/release/xks-proxy /usr/sbin/xks-proxy 30 | 31 | FROM ubuntu 32 | 33 | COPY --from=builder /etc/softhsm/ /etc/softhsm/ 34 | COPY --from=builder /var/lib/softhsm/ /var/lib/softhsm/ 35 | 36 | COPY --from=builder /usr/lib/ /usr/lib/ 37 | COPY --from=builder /usr/bin/ /usr/bin/ 38 | 39 | COPY --from=builder /var/local/ /var/local/ 40 | 41 | COPY --from=builder /usr/sbin/xks-proxy /usr/sbin/xks-proxy 42 | 43 | EXPOSE 80 44 | 45 | ENV XKS_PROXY_SETTINGS_TOML=/var/local/xks-proxy/.secret/settings.toml \ 46 | RUST_BACKTRACE=1 47 | 48 | ENTRYPOINT ["/usr/sbin/xks-proxy"] 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | SPECFILE_NAME := xks-proxy.spec 5 | SPECFILE := rpmspec/$(SPECFILE_NAME) 6 | NAME := $(shell rpmspec -q --queryformat "%{name}" $(SPECFILE)) 7 | VERSION := $(shell rpmspec -q --queryformat "%{version}" $(SPECFILE)) 8 | RELEASE := $(shell rpmspec -q --queryformat "%{release}" $(SPECFILE)) 9 | TOPDIR := $(shell rpm --eval "%{_topdir}" ) 10 | RPMDIR := $(shell rpm --eval "%{_rpmdir}" ) 11 | SPECDIR := $(shell rpm --eval "%{_specdir}" ) 12 | SOURCESDIR := $(shell rpm --eval "%{_sourcedir}") 13 | BUILDROOT := $(shell rpm --eval %{_buildrootdir}) 14 | SOURCE_BUNDLE := $(NAME)-$(VERSION)-$(RELEASE).tar.xz 15 | PROJECT_DIR := xks-axum 16 | PROJECT_ROOTDIR := $(shell basename $(CURDIR)) 17 | APP_FROM_DIR := $(PROJECT_DIR)/target/release 18 | 19 | RPMBUILDDIR_APP := $(BUILDROOT)/usr/sbin 20 | RPMBUILD_APP := $(RPMBUILDDIR_APP)/$(NAME) 21 | 22 | RPMBUILDIR_SERVICE_UNIT := $(BUILDROOT)/etc/systemd/system 23 | RPMBUILD_SERVICE_UNIT := $(RPMBUILDIR_SERVICE_UNIT)/$(NAME).service 24 | 25 | RPMBUILD_SPECFILE := $(SPECDIR)/$(SPECFILE_NAME) 26 | RPMBUILD_SOURCEFILE := $(SOURCESDIR)/$(SOURCE_BUNDLE) 27 | RPM := $(RPMDIR)/x86_64/$(NAME)-$(VERSION)-$(RELEASE).x86_64.rpm 28 | DEB := $(NAME)-$(VERSION)-$(RELEASE).deb 29 | 30 | ifneq (, $(shell git rev-parse --is-inside-work-tree 2>/dev/null)) 31 | export GIT_HASH := $(shell git rev-parse --short HEAD) 32 | endif 33 | 34 | .PHONY: release 35 | release: $(RPM) 36 | 37 | $(RPM): $(RPMBUILD_SPECFILE) $(RPMBUILD_SOURCEFILE) $(RPMBUILD_APP) $(RPMBUILD_SERVICE_UNIT) 38 | rpmbuild -ba --noclean $(RPMBUILD_SPECFILE) --buildroot=$(BUILDROOT) 39 | # Symlink to the rpm so we can use a "constant" name by CodeBuild in buildspec.yml 40 | # and CodeDeploy in appspec.yml 41 | ln -s $(RPM) $(APP_FROM_DIR)/aws-kms-xks-proxy.rpm 42 | 43 | ifeq (, $(shell which alien 2>/dev/null)) 44 | @echo "INFO: No command alien found" 45 | else 46 | ifeq (, $(shell which sudo)) 47 | alien $(RPM) 48 | else 49 | sudo alien $(RPM) 50 | endif 51 | endif 52 | 53 | $(RPMBUILD_SPECFILE): 54 | for dir in BUILD RPMS SOURCES SPECS SRPMS; do \ 55 | mkdir -p ~/rpmbuild/$$dir; \ 56 | done 57 | cp $(SPECFILE) $@ 58 | 59 | $(RPMBUILD_SOURCEFILE): 60 | cargo clean --manifest-path=$(PROJECT_DIR)/Cargo.toml && \ 61 | cd .. && \ 62 | tar cJfh $@ \ 63 | --exclude=$(PROJECT_ROOTDIR)/.git \ 64 | --exclude=$(PROJECT_ROOTDIR)/.gitignore \ 65 | --exclude=$(PROJECT_ROOTDIR)/Config \ 66 | $(PROJECT_ROOTDIR) 67 | 68 | $(RPMBUILD_APP): $(APP_FROM_DIR)/$(NAME) 69 | mkdir -p $(RPMBUILDDIR_APP) 70 | cp scripts/* $(RPMBUILDDIR_APP)/ 71 | cp $(APP_FROM_DIR)/$(NAME) $@ 72 | 73 | $(APP_FROM_DIR)/$(NAME): 74 | ifeq (, $(shell git rev-parse --is-inside-work-tree 2>/dev/null)) 75 | @echo "INFO: Not a git repository" 76 | endif 77 | cargo build --release --manifest-path=$(PROJECT_DIR)/Cargo.toml 78 | 79 | $(RPMBUILD_SERVICE_UNIT): 80 | mkdir -p $(RPMBUILDIR_SERVICE_UNIT) 81 | cp systemd/* $(RPMBUILDIR_SERVICE_UNIT)/ 82 | 83 | # A convenient target to install all the necessary rpm build tools 84 | .PHONY: install_rpm_tools 85 | install_rpm_tools: 86 | sudo yum install -y rpmdevtools rpm-build rpm-devel rpmlint 87 | @touch $@ 88 | 89 | .PHONY: clean 90 | clean: 91 | rm -f $(RPM) 92 | rm -rf $(APP_FROM_DIR) 93 | 94 | .PHONY: distclean 95 | distclean: clean 96 | rm -rf $(TOPDIR) 97 | rm -f install_rpm_tools 98 | 99 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/aws-samples/aws-kms-xks-proxy/actions/workflows/makefile.yml/badge.svg) 2 | 3 | ## aws-kms-xks-proxy 4 | 5 | This package provides a sample implementation of the [AWS KMS External Keystore (XKS) Proxy API](https://github.com/aws/aws-kms-xksproxy-api-spec) for reference by external customers against any [Hardware Security Module](https://en.wikipedia.org/wiki/Hardware_security_module) (HSM) that supports [PKCS#11 v2.40](http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html). 6 | 7 | The current implementation is written in [Rust](https://www.rust-lang.org/) and is known to work with the following types of HSM: 8 | * `LunaSA 7.4.0` (via Thales eLab) 9 | * `Luna 7 Network HSM` (hardware device) 10 | * `Entrust nShield XC HSM` 11 | * [SoftHSMv2](https://github.com/opendnssec/SoftHSMv2) 12 | * [AWS CloudHSM](https://aws.amazon.com/cloudhsm/) 13 | 14 | ## Quick Start 15 | 16 | Build `xks-proxy` into a [Docker](https://www.docker.com/) image to serve http traffic, with [SoftHSMv2](https://github.com/opendnssec/SoftHSMv2) pre-installed and pre-generated with a single AES key named `foo`: 17 | ```bash 18 | docker build -t xks-proxy:latest . 19 | ``` 20 | Run `xks-proxy` as a Docker container: 21 | ```bash 22 | docker run --rm --name xks-proxy -d -p 0.0.0.0:80:80 xks-proxy:latest 23 | ``` 24 | Verify the server is up and running: 25 | ```bash 26 | curl localhost/ping 27 | ``` 28 | You should see the response: 29 | 30 | >pong from xks-proxy v3.1.2-unknown 31 | 32 | which indicates the server is now up and running. You can monitor the logs with: 33 | ```bash 34 | docker logs -f xks-proxy 35 | ``` 36 | Using another terminal, download a [curl](https://curl.se/) based client and `cd` to it: 37 | ``` bash 38 | git clone https://github.com/aws-samples/aws-kms-xksproxy-test-client.git 39 | cd aws-kms-xksproxy-test-client 40 | ``` 41 | (Note you'd need to have `curl 7.75+` installed for the following.) 42 | 43 | Run tests locally against the `xks-proxy`: 44 | ```bash 45 | export XKS_PROXY_HOST="localhost" 46 | export URI_PREFIX="example/uri/path/prefix" 47 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 48 | export SIGV4_ACCESS_KEY_ID="BETWEEN2TENAND3TENCHARACTERS" 49 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 50 | export SIGV4_SECRET_ACCESS_KEY="PleaseReplaceThisWithSomeSecretOfLength43To64" 51 | export KEY_ID="foo" 52 | export SCHEME= 53 | ./test-xks-proxy 54 | ``` 55 | You should see `All 35 tests PASSED` at the end. 56 | 57 | To stop the Docker container running `xks-proxy`: 58 | ```bash 59 | docker container stop xks-proxy 60 | ``` 61 | More information about the use of docker can be found at [docker/README.md](docker/README.md). 62 | 63 | ## Details 64 | 65 | The [XKS Proxy API spec](https://github.com/aws/aws-kms-xksproxy-api-spec) assumes you have already set up the vendor specific HSM with the necessary cryptographic keys created. Once that is done, set up a configuration file at `/var/local/xks-proxy/.secret/settings.toml`. A number of sample configuration files for different types of HSM's can be found under the `xks-axum/configuration` folder. 66 | 67 | Then, to run the XKS Proxy server for debugging purposes: 68 | 69 | ```bash 70 | # Under the xks-axum directory 71 | XKS_PROXY_SETTINGS_TOML=/var/local/xks-proxy/.secret/settings.toml cargo run 72 | ``` 73 | 74 | ### Unit tests 75 | 76 | To run the unit tests, type 77 | ```bash 78 | # Under the xks-axum directory 79 | cargo test 80 | ``` 81 | ### Generate [RPM](https://en.wikipedia.org/wiki/RPM_Package_Manager) for Centos Linux 82 | 83 | To generate the [RPM](https://en.wikipedia.org/wiki/RPM_Package_Manager) for `xks-proxy` for installation on a `Centos Linux x86_64` platform, type: 84 | ```bash 85 | # Under the root directory 86 | make 87 | ``` 88 | 89 | ### Install via RPM 90 | 91 | To install the generated [RPM](https://en.wikipedia.org/wiki/RPM_Package_Manager) on a `Centos Linux x86_64` platform: 92 | ```bash 93 | # For example 94 | sudo yum install -y xks-proxy-3.1.2-0.el7.x86_64.rpm 95 | ``` 96 | 97 | ### Configuration 98 | 99 | Specify the configuration file path, such as `/var/local/xks-proxy/.secret/settings.toml`, by setting the environment variable `XKS_PROXY_SETTINGS_TOML`. 100 | 101 | See the `xks-axum/configuration` folders for some sample configuraions for various HSM's such as `Thales Luna HSM`, `Entrust nShield HSM`, `AWS CloudHSM` and `SoftHSMv2`. 102 | 103 | ### Manage `xks-proxy` as a systemd unit 104 | 105 | Once the `xks-proxy` RPM has been installed, you can manage it as a `systemd unit`. For example 106 | 107 | ```bash 108 | # Show the backing file 109 | sudo systemctl cat xks-proxy 110 | 111 | # Display the status 112 | systemctl status xks-proxy 113 | 114 | # The default systemd unit configuration for xks-proxy can be found at /etc/systemd/system/xks-proxy.service. 115 | # To override the configuration: 116 | sudo systemctl edit xks-proxy 117 | 118 | # To start xks-proxy 119 | sudo systemctl start xks-proxy 120 | 121 | # To stop 122 | sudo systemctl stop xks-proxy 123 | 124 | # Display the system journal log 125 | sudo journalctl -xef --unit xks-proxy 126 | ``` 127 | 128 | ### Clean logs via `xks-proxy_cleanlogs.timer` 129 | 130 | If you choose to output logging of `xks-proxy` to a file (with `is_file_writer_enabled = true` in `settings.toml`), you can have the log files automatically removed perodically via a `systemd timer`. 131 | 132 | ```bash 133 | # Show the backing file for the timer 134 | sudo systemctl cat xks-proxy_cleanlogs.timer 135 | 136 | # Display the status of the cleaning service driven by the timer 137 | systemctl status xks-proxy_cleanlogs 138 | 139 | # Display the status of the timer 140 | systemctl status xks-proxy_cleanlogs.timer 141 | 142 | # The default systemd unit configuration for the timer can be found at /etc/systemd/system/xks-proxy_cleanlogs.timer. 143 | # To override the (default hourly timer) configuration: 144 | sudo systemctl edit xks-proxy_cleanlogs.timer 145 | 146 | # To start the timer 147 | sudo systemctl start xks-proxy_cleanlogs.timer 148 | 149 | # Display the system journal log of the cleaning service 150 | sudo journalctl -xef --unit xks-proxy_cleanlogs 151 | 152 | # Display the system journal log of the timer 153 | sudo journalctl -xef --unit xks-proxy_cleanlogs.timer 154 | ``` 155 | 156 | ## Security 157 | 158 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 159 | 160 | ## License 161 | 162 | This project is licensed under the Apache-2.0 License. 163 | -------------------------------------------------------------------------------- /appspec.yml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This appspec.yml file controls the behavior of CodeDeploy in a pipeline environment, 5 | # and must be pushed to the CodeCommit source repository to become effective. 6 | version: 0.0 7 | os: linux 8 | files: 9 | # Note the buildspec.yml for CodeBuild specifies an absolute path for the output artifact 10 | # whereas here we need to specify a relative path to be used by CodeDeploy on the deployed host. 11 | - source: xks-axum/target/release/aws-kms-xks-proxy.rpm 12 | # File path on the deployed host to put the above artifact 13 | destination: /tmp 14 | hooks: 15 | BeforeInstall: 16 | - location: codepipeline/scripts/before-install 17 | timeout: 5 18 | # Note the proper version of rpm is made available by CodeDeploy 19 | # only after the installation at AfterInstall, not before. 20 | AfterInstall: 21 | - location: codepipeline/scripts/after-install 22 | timeout: 10 23 | ApplicationStart: 24 | - location: codepipeline/scripts/application-start 25 | timeout: 5 26 | ValidateService: 27 | - location: codepipeline/scripts/validate-service 28 | timeout: 5 29 | -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | version: 0.2 5 | 6 | #env: 7 | #variables: 8 | # key: "value" 9 | # key: "value" 10 | #parameter-store: 11 | # key: "value" 12 | # key: "value" 13 | #secrets-manager: 14 | # key: secret-id:json-key:version-stage:version-id 15 | # key: secret-id:json-key:version-stage:version-id 16 | #exported-variables: 17 | # - variable 18 | # - variable 19 | #git-credential-helper: yes 20 | #batch: 21 | #fast-fail: true 22 | #build-list: 23 | #build-matrix: 24 | #build-graph: 25 | phases: 26 | install: 27 | commands: 28 | # - yum update -y 29 | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 30 | - source $HOME/.cargo/env 31 | #pre_build: 32 | #commands: 33 | # - command 34 | # - command 35 | build: 36 | commands: 37 | - mkdir /root/rpmbuild 38 | - make install_rpm_tools release 39 | # - echo -e "%_topdir /root/rpmbuild" > /root/.rpmmacros 40 | #post_build: 41 | #commands: 42 | # - command 43 | # - command 44 | #reports: 45 | #report-name-or-arn: 46 | #files: 47 | # - location 48 | # - location 49 | #base-directory: location 50 | #discard-paths: yes 51 | #file-format: JunitXml | CucumberJson 52 | artifacts: 53 | files: 54 | # include the appspec.yml from the git repo 55 | - appspec.yml 56 | 57 | # include all the scripts from the git repo 58 | - codepipeline/**/* 59 | 60 | # include all the rpm from the git repo 61 | - xks-axum/target/release/aws-kms-xks-proxy.rpm 62 | 63 | # Actual locations of the rpm files 64 | # - $HOME/rpmbuild/RPMS/**/*.rpm 65 | # - $HOME/rpmbuild/SRPMS/*.rpm 66 | #name: $(date +%Y-%m-%d) 67 | #discard-paths: yes 68 | #base-directory: location 69 | #cache: 70 | #paths: 71 | # - paths 72 | -------------------------------------------------------------------------------- /codepipeline/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | This folder stores the scripts used by `AWS CodeDeploy` in a CodePipeline environment. The scripts become available to `AWS CodeDeploy` in a pipeline once the content of this folder is pushed to `AWS CodeCommit`. 7 | 8 | Note this directory and the `appspec.yml` in the root directory are closely related. Together they must be pushed to `AWS CodeCommit` to get used by `AWS CodeDeploy` in a pipeline environment. 9 | 10 | -------------------------------------------------------------------------------- /codepipeline/scripts/after-install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | FOLDER=/tmp/codedeploy 7 | cd $FOLDER || { echo "Failed to cd $FOLDER" >> after-install.txt; exit 1; } 8 | XKSPROXY_RPM="aws-kms-xks-proxy.rpm" 9 | 10 | { 11 | mv /tmp/$XKSPROXY_RPM . 12 | sha256sum $XKSPROXY_RPM 13 | 14 | echo "yum removing xks-proxy" 15 | yum remove -y xks-proxy 16 | 17 | echo "yum installing xks-proxy" 18 | yum install -y $XKSPROXY_RPM || { echo "Failed to install xks-proxy"; exit 1; } 19 | 20 | echo "The AfterInstall deployment lifecycle event successfully completed." 21 | } >> after-install.txt 2>&1 22 | -------------------------------------------------------------------------------- /codepipeline/scripts/application-start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | FOLDER=/tmp/codedeploy 7 | cd $FOLDER || { echo "Failed to cd $FOLDER" >> application-start.txt; exit 1; } 8 | 9 | { 10 | echo "Starting xks-proxy" 11 | systemctl start xks-proxy || { echo "Failed to start xks-proxy"; exit 1; } 12 | 13 | echo "Enabling xks-proxy" 14 | systemctl enable xks-proxy || { echo "Failed to enable xks-proxy"; exit 1; } 15 | 16 | echo "Starting xks-proxy_cleanlogs.timer" 17 | systemctl start xks-proxy_cleanlogs.timer || { echo "Failed to start xks-proxy_cleanlogs.timer"; exit 1; } 18 | 19 | echo "Enabling xks-proxy_cleanlogs.timer" 20 | systemctl enable xks-proxy_cleanlogs.timer || { echo "Failed to enable xks-proxy_cleanlogs.timer"; exit 1; } 21 | 22 | echo "The ApplicationStart deployment lifecycle event successfully completed." 23 | } >> application-start.txt 2>&1 24 | -------------------------------------------------------------------------------- /codepipeline/scripts/before-install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | FOLDER=/tmp/codedeploy 7 | if [ -d $FOLDER ] 8 | then 9 | rm -rf $FOLDER 10 | fi 11 | 12 | mkdir -p $FOLDER 13 | cd $FOLDER || { echo "Failed to cd $FOLDER" >> before-install.txt; exit 1; } 14 | echo "The BeforeInstall deployment lifecycle event successfully completed." >> before-install.txt 15 | -------------------------------------------------------------------------------- /codepipeline/scripts/validate-service: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | FOLDER=/tmp/codedeploy 7 | cd $FOLDER || { echo "Failed to cd $FOLDER" >> validate-service.txt; exit 1; } 8 | 9 | { 10 | systemctl status xks-proxy xks-proxy_cleanlogs.service xks-proxy_cleanlogs.timer 11 | echo "The ValidateService deployment lifecycle event successfully completed." 12 | } >> validate-service.txt 2>&1 13 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Overview 7 | 8 | An outline and some examples on how to build a docker image for `xks-proxy` and how the image can be run in a docker container. This document assumes you have docker installed. 9 | 10 | ## How to build a docker image for `xks-proxy`? 11 | 12 | 1. Under the project directory: 13 | 14 | docker build -t xks-proxy:latest . 15 | 1. Save the image to a tar file, if it needs to be exported/shared: 16 | 17 | docker save -o xks-proxy-docker-v3.1.2.tar xks-proxy:latest 18 | 1. Compress `xks-proxy-docker-v3.1.2.tar` into `xks-proxy-docker-v3.1.2.tar.xz` if necessary: 19 | 20 | xz -z -0 xks-proxy-docker-v3.1.2.tar 21 | 22 | ## How to run `xks-proxy` in a docker container? 23 | 24 | 1. Decompress `xks-proxy-docker-v3.1.2.tar.xz` to `xks-proxy-docker-v3.1.2.tar` if necessary: 25 | 26 | xz -d xks-proxy-docker-v3.1.2.tar.xz 27 | 1. Load the docker image if necessary: 28 | 29 | docker load -i xks-proxy-docker-v3.1.2.tar 30 | 1. Run `xks-proxy` in a docker container exposing port `80` (of the container) as port `80` on the running host: 31 | 32 | docker run --name xks-proxy -d -p 0.0.0.0:80:80 xks-proxy:latest 33 | 1. Now you can access it at 34 | `http:///example/uri/path/prefix/kms/xks/v1` 35 | or whatever URI path you've configured in `settings.toml`. 36 | 37 | ## Cheat sheet 38 | 39 | * Remove the `xks-proxy` docker image: 40 | 41 | docker rmi xks-proxy:latest 42 | * Exec into the `xks-proxy` docker container: 43 | 44 | docker exec -it xks-proxy bash 45 | * List docker images: 46 | 47 | docker images 48 | * List docker containers: 49 | 50 | docker container ls 51 | * Ping `xks-proxy` running in docker container 52 | 53 | # should get back a "pong from xks-proxy v3.1.2" response 54 | curl http://localhost/ping 55 | * Follow the log of the running `xks-proxy` in the docker container 56 | 57 | docker logs -f xks-proxy 58 | -------------------------------------------------------------------------------- /rpmspec/xks-proxy.spec: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | Name: xks-proxy 5 | Version: 3.1.2 6 | 7 | Release: 0%{?dist} 8 | Summary: AWS External Keystore (XKS) Proxy Service 9 | 10 | License: Apache-2.0 11 | Vendor: Amazon.com, Inc. 12 | URL: TBD 13 | Source0: %{name}-%{version}-%{release}.tar.xz 14 | 15 | %description 16 | AWS External Keystore (XKS) Proxy as a systemd service unit. 17 | 18 | AWS External Key Stores allow customers to protect their data in AWS 19 | using cryptographic keys held inside on-premises Hardware Security Modules (HSMs) 20 | or other key managers outside of AWS. 21 | 22 | This service implements the XKS Proxy API specification allowing AWS KMS 23 | to access a custom external keystore using a uniform interface. 24 | 25 | %clean 26 | 27 | %files 28 | /etc/systemd/system/xks-proxy.service 29 | /etc/systemd/system/xks-proxy_cleanlogs.service 30 | /etc/systemd/system/xks-proxy_cleanlogs.timer 31 | /usr/sbin/xks-proxy 32 | /usr/sbin/xks-proxy_cleanlogs 33 | 34 | %pre 35 | 36 | %post 37 | systemctl daemon-reload 38 | systemctl enable xks-proxy.service 39 | systemctl enable xks-proxy_cleanlogs.timer 40 | 41 | %preun 42 | systemctl stop xks-proxy.service 43 | systemctl stop xks-proxy_cleanlogs.timer 44 | systemctl disable xks-proxy.service 45 | systemctl disable xks-proxy_cleanlogs.timer 46 | 47 | %changelog 48 | * Mon Nov 28 2022 Hanson Char - 3.1.2 49 | - Initialize pkcs11 context with lock functions and upon failure would 50 | retry pkcs11 initialization without callback functions 51 | - Fix memory corruption bugs in pkcs11 crate in context initialization 52 | - Fix bug in closing pkcs11 session 53 | - Mark Ctx::new_and_initialize and Ctx::initialize as unsafe 54 | - Fix doc inconsistency 55 | - Log pool exhaustion as a warning instead of info 56 | - Avoid removing session from pool unnecessarily 57 | - Always attempt to login when creating a new session 58 | - Respond with InternalException instead of KeyNotFoundException upon CKR_GENERAL_ERROR. 59 | - Include git hash in version + remove noise if command alien is not found 60 | * Tue Nov 22 2022 Hanson Char - 3.1.1 61 | - Always return ValidationException upon invalid JSON payload 62 | * Wed Nov 09 2022 Hanson Char - 3.1.0 63 | - Always enable http ping for load balancer 64 | * Wed Sep 21 2022 Hanson Char - 3.0.0 65 | - Support full configurable of TCP keepalive probes 66 | * Sun Sep 11 2022 Hanson Char - 2.0.1 67 | - Support configurable interval to send TCP keepalive probes 68 | * Thu Sep 08 2022 Hanson Char - 2.0.0 69 | - Rename sigv4_access_id to sigv4_access_key_id and sigv4_secret_key to sigv4_secret_access_key 70 | * Thu Aug 25 2022 Hanson Char - 1.0.1 71 | - Remove local patch of scratchstack-aws-signature as panic bug is fixed in v0.10.4 72 | * Tue Aug 23 2022 Hanson Char - 1.0.0 73 | - Support verifying client dns name when mTLS is enabled 74 | - Fix tar command for source rpm 75 | * Sat Aug 20 2022 Hanson Char - 0.3.2 76 | - Improve docker/README.md 77 | - Display version upon startup and /pong 78 | * Tue Aug 16 2022 Hanson Char - 0.3.1 79 | - Fix panic at scratchstack-aws-signature due to index out of bounds: https://github.com/dacut/scratchstack-aws-signature/issues/2 80 | - Upgrade serila_test* dependencies + fix per clippy. 81 | * Wed Aug 03 2022 Hanson Char - 0.3.0 82 | - Always append a 2-byte AAD length of the input AAD to the AAD as input to the HSM per latest API spec. 83 | - Fix ciphertextMetadata length to be a single byte prior to appending to the AAD as input to the HSM. 84 | * Mon Jul 18 2022 Hanson Char - 0.2.0 85 | - Always append length of ciphertextMetadata to AAD per latest API spec 86 | * Tue Jul 05 2022 Hanson Char - 0.1.5 87 | - Remove errorMessage from http JSON response per latest API spec 88 | * Wed Jun 29 2022 Hanson Char - 0.1.4 89 | - Support CDIV per latest API spec 90 | * Tue Jun 21 2022 Hanson Char - 0.1.3 91 | - Support rotation_kind in settings.toml 92 | * Tue Jun 21 2022 Hanson Char - 0.1.2 93 | - Add is_stdout_writer_enabled and is_file_writer_enabled to settings.toml 94 | * Fri Jun 17 2022 Hanson Char - 0.1.1 95 | - Add systemd timer for log cleaning 96 | * Mon Mar 21 2022 Hanson Char - 0.1.0 97 | - Initial release 98 | -------------------------------------------------------------------------------- /scripts/xks-proxy_cleanlogs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | declare -r directory=/var/local/xks-proxy/logs 7 | 8 | log_stderr() { 9 | echo "$*" >&2 10 | } 11 | 12 | abort() { 13 | log_stderr "$*" 14 | exit 1 15 | } 16 | 17 | number_of_xksproxy_log_files() { 18 | if [[ ! -d "$directory" ]]; then 19 | echo 0 20 | return 21 | fi 22 | 23 | local -i count=0 24 | for _ in "$directory"/xks-proxy.log.*; do 25 | ((++count)) 26 | done 27 | 28 | echo "$count" 29 | } 30 | 31 | main() { 32 | # LOG_RETENTION_NUMBER can be configured via the xks-proxy_cleanlogs.service systemd unit file 33 | local -ir default_log_retention=${LOG_RETENTION_NUMBER:-24} 34 | local -ir log_retention_number=$(( default_log_retention > 0 ? default_log_retention : 1 )) 35 | local -ir delete_count=$(( $(number_of_xksproxy_log_files) - log_retention_number )) 36 | local -i deleted=0 37 | if (( delete_count > 0 )); then 38 | for file in "$directory"/xks-proxy.log.*; do 39 | log_stderr "removing $file" 40 | rm "$file" || abort "failed to remove $file with status code $?" 41 | (( ++deleted < delete_count )) || break 42 | done 43 | fi 44 | echo "$deleted" 45 | } 46 | 47 | declare -i deleted_count 48 | deleted_count=$(main) 49 | echo "$0" completed successfully with "$deleted_count" files removed 50 | -------------------------------------------------------------------------------- /systemd/xks-proxy.service: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [Unit] 5 | Description=AWS External Keystore (XKS) Proxy Service 6 | 7 | [Service] 8 | # Specifies the configuration file for rust-xks-proxy 9 | Environment=XKS_PROXY_SETTINGS_TOML=/var/local/xks-proxy/.secret/settings.toml 10 | # Prints a backtrace to stderr whenever a panic occurs 11 | Environment=RUST_BACKTRACE=1 12 | 13 | # You can use the following environment variables to override the PKCS11 related 14 | # configurations 15 | 16 | # Specifies the file path to the PKCS#11 library. 17 | # Environment=PKCS11_HSM_MODULE=/local/centos/pkcs11-logger/build/linux/pkcs11-logger-x64.so 18 | 19 | # https://github.com/Pkcs11Interop/pkcs11-logger 20 | # Path to the original PKCS#11 library. 21 | # Environment=PKCS11_LOGGER_LIBRARY_PATH=/usr/safenet/lunaclient/lib/libCryptoki2_64.so 22 | 23 | # Path to the pkcs11-logger log file. 24 | # Environment=PKCS11_LOGGER_LOG_FILE_PATH=/var/local/xks-proxy/logs/pkcs11-logger-output.log 25 | 26 | # Specifies a bit mask that controls multiple pkcs11-logger features. 27 | # Environment=PKCS11_LOGGER_FLAGS=0 28 | 29 | ExecStart=/usr/sbin/xks-proxy 30 | 31 | [Install] 32 | WantedBy=multi-user.target 33 | -------------------------------------------------------------------------------- /systemd/xks-proxy_cleanlogs.service: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This service unit is for cleaning up xks-proxy log files 5 | 6 | [Unit] 7 | Description=Clean up xks-proxy log files 8 | 9 | [Service] 10 | Type=oneshot 11 | ExecStart=/usr/sbin/xks-proxy_cleanlogs 12 | 13 | # Number of xks-proxy log files to keep 14 | Environment=LOG_RETENTION_NUMBER=24 15 | -------------------------------------------------------------------------------- /systemd/xks-proxy_cleanlogs.timer: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This timer unit is for cleaning up xks-proxy log files 5 | 6 | [Unit] 7 | Description=Clean up xks-proxy log files 8 | 9 | [Timer] 10 | # DOW YYYY-MM-DD HH:MM:SS 11 | OnCalendar=*-*-* *:00:00 12 | 13 | [Install] 14 | WantedBy=timers.target 15 | -------------------------------------------------------------------------------- /xks-axum/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | [package] 5 | name = "xks-proxy" 6 | version = "3.1.2" 7 | edition = "2018" 8 | license = "Apache-2.0" 9 | publish = false 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | [dependencies] 13 | axum = { version = "0.6", features = ["headers"] } 14 | axum-server = { version = "0.4", features = ["tls-rustls"] } 15 | axum-macros = "0.3" 16 | 17 | base64 = "0.21" 18 | bytes = "1.1" 19 | chrono = "0.4" 20 | const_format = "0.2" 21 | deadpool = { version = "0.9", features = ["rt_tokio_1", "unmanaged"] } 22 | 23 | hex = "0.4.2" 24 | 25 | http = "0.2" 26 | http-body = "0.4" 27 | hyper = "0.14" 28 | 29 | lazy_static = "1.4" 30 | 31 | oso = "0.26" 32 | oso-derive = "0.26" 33 | 34 | parking_lot = "0.12" 35 | pkcs11 = "0.5.0" 36 | 37 | ring = "0.16" 38 | 39 | rustls = "0.20" 40 | rustls-pemfile = "1.0" 41 | 42 | scratchstack-aws-signature = "=0.10.5" 43 | 44 | serde = { version = "1.0", features = ["derive"] } 45 | serde_derive = "1.0" 46 | serde_json = "1.0" 47 | serde_with = "2.0" 48 | 49 | tokio = { version = "1", features = ["rt-multi-thread"]} 50 | toml = "0.7" 51 | tonic = { version = "0.8", features = ["transport", "tls"] } 52 | tower-http = { version = "0.3", features = ["trace"] } 53 | 54 | tracing = "0.1" 55 | tracing-subscriber = "0.3" 56 | tracing-appender = "0.2" 57 | 58 | webpki = "0.22" 59 | 60 | [dev-dependencies] 61 | serial_test = "1.0" 62 | serial_test_derive = "1.0" 63 | 64 | [patch.crates-io] 65 | # Patch to fix segmentation fault upon loading and initializing the libCryptoki2_64.so 66 | # https://github.com/mheese/rust-pkcs11/issues/50 67 | pkcs11 = { path = "rust-pkcs11" } 68 | 69 | [profile.dev] 70 | panic = "abort" 71 | 72 | [profile.release] 73 | panic = "abort" 74 | -------------------------------------------------------------------------------- /xks-axum/configuration/bootstrap.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # Path to the xks-proxy configuration file for unit testing purposes. 5 | # This is also used as the default path to the xks-proxy configuration file 6 | # if the XKS_PROXY_SETTINGS_TOML environment variable is not set. 7 | XKS_PROXY_SETTINGS_TOML = "configuration/settings.toml" 8 | -------------------------------------------------------------------------------- /xks-axum/configuration/oso.polar: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Sample polar file for testing secondary authorization. More details at: 5 | # 6 | # https://docs.osohq.com/rust/reference/polar/polar-syntax.html 7 | # https://docs.osohq.com/rust/reference/polar/classes.html 8 | 9 | # Secondary auth for "encrypt" requests 10 | allow(uri_path_prefix: String, "encrypt", metadata: EncryptMetadata) 11 | if uri_path_prefix != "" 12 | # and metadata.awsPrincipalArn = "alice" 13 | and metadata.kmsOperation = "Encrypt" 14 | and (metadata.kmsViaService = nil 15 | or "ebs" in metadata.kmsViaService); 16 | 17 | # Secondary auth for "decrypt" requests 18 | allow(uri_path_prefix: String, "decrypt", metadata: EncryptMetadata) 19 | if uri_path_prefix != "" 20 | # and metadata.awsPrincipalArn = "bob" 21 | and metadata.kmsOperation in ["Encrypt", "Decrypt"] 22 | and (metadata.kmsViaService = nil 23 | or "ebs" in metadata.kmsViaService); 24 | 25 | # Secondary auth for "metadata" requests 26 | allow(uri_path_prefix: String, "metadata", metadata: GetKeyMetadata) 27 | if uri_path_prefix != "" 28 | and metadata.kmsOperation = "DescribeKey"; 29 | 30 | # Secondary auth for "health" requests 31 | allow(uri_path_prefix: String, "health", metadata: GetHealthMetadata) 32 | if uri_path_prefix != "" 33 | and metadata.kmsOperation in [ 34 | "CreateCustomKeystore", 35 | "ConnectCustomKeystore", 36 | "UpdateCustomKeystore", 37 | ]; 38 | -------------------------------------------------------------------------------- /xks-axum/configuration/settings.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This an xks-proxy configuration file of using SoftHSMv2 for unit testing purposes. 5 | # It assumes you have installed SoftHSMv2, and have placed its shared library at 6 | # /usr/local/lib/softhsm/libsofthsm2.so 7 | 8 | [server] 9 | ip = "0.0.0.0" 10 | port = 80 11 | # (Optional) port used for http ping. Defaults to 80. 12 | # port_http_ping = 80 13 | region = "us-east-1" 14 | service = "kms-xks-proxy" 15 | # Optional configuration of ciphertext metadata in base 64 encoding 16 | # ciphertext_metadata_b64 = "djAuMC4x" 17 | 18 | # Configuration of TCP keepalive probes 19 | # https://en.wikipedia.org/wiki/Keepalive 20 | [server.tcp_keepalive] 21 | # (Optional) Number of seconds between two keepalive transmissions in idle condition 22 | # No configuration means TCP keepalive probes is disabled. 23 | tcp_keepalive_secs = 60 24 | # (Optional) Number of retransmissions to be carried out before declaring that remote end is not available 25 | tcp_keepalive_retries = 3 26 | # (Optional) Number of seconds between two successive keepalive retransmissions, 27 | # if acknowledgement to the previous keepalive transmission is not received 28 | tcp_keepalive_interval_secs = 1 29 | 30 | [tracing] 31 | # Used to control logging to stdout 32 | is_stdout_writer_enabled = true 33 | # Used to control logging to a file (rotated hourly) 34 | is_file_writer_enabled = true 35 | 36 | # Supported trace levels: TRACE, DEBUG, INFO, WARN, ERROR 37 | # Should be set to INFO for production. 38 | level = "DEBUG" 39 | 40 | # Directory and file prefix, applicable only if is_file_writer_enabled = true 41 | directory = "/var/local/xks-proxy/logs" 42 | file_prefix = "xks-proxy.log" 43 | # Supported rotation: MINUTELY, HOURLY, DAILY, NEVER 44 | # Should never be set to NEVER in production. 45 | # Adjust the timer calender of xks-proxy_cleanlogs.timer via "systemctl edit xks-proxy_cleanlogs.timer" as needed. 46 | rotation_kind = "HOURLY" 47 | 48 | [security] 49 | # is_sigv4_auth_enabled must be set to true for production. 50 | is_sigv4_auth_enabled = true 51 | is_tls_enabled = false 52 | is_mtls_enabled = false 53 | 54 | # (Optional) secondary authorization used 55 | secondary_auth = "Oso" 56 | 57 | [security.oso] 58 | polar_file_path = "configuration/oso.polar" 59 | 60 | [tls] 61 | # Applicable when is_tls_enabled = true 62 | tls_cert_pem = "tls/server_cert.pem" 63 | tls_key_pem = "tls/server_key.pem" 64 | # Applicable when is_mtls_enabled = true 65 | mtls_client_ca_pem = "tls/client_ca.pem" 66 | mtls_client_dns_name = "us-east-1.alpha.cks.kms.aws.internal.amazonaws.com" 67 | 68 | [[external_key_stores]] 69 | # Each uri path prefix defines a logical xks, and therefore every uri path prefix must be unique. 70 | # A Proxy URI path prefix is either empty, or it must have between 9 and 117 characters. 71 | # Valid characters are a-z, A-Z, 0-9, /, - (hyphen), and _ (underscore) 72 | uri_path_prefix = "" 73 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 74 | sigv4_access_key_id = "" 75 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 76 | sigv4_secret_access_key = "" 77 | # Each xks key id must exist in the underlying HSM with the corresponding pkcs11 label 78 | xks_key_id_set = ["foo", "cat", "dog"] 79 | 80 | [[external_key_stores]] 81 | uri_path_prefix = "/example/uri/path/prefix" 82 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 83 | sigv4_access_key_id = "" 84 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 85 | sigv4_secret_access_key = "" 86 | xks_key_id_set = ["foo", "bar"] 87 | 88 | [pkcs11] 89 | session_pool_max_size = 30 90 | session_pool_timeout_milli = 0 91 | # Set to true for testing purposes only 92 | session_eager_close = false 93 | user_pin = "1234" 94 | # Default value for the PKCS11_HSM_MODULE environment variable that 95 | # specifies the file path to the PKCS#11 library. 96 | PKCS11_HSM_MODULE = "/usr/local/lib/softhsm/libsofthsm2.so" 97 | # Number of milli seconds before a read access to the pkcs#11 context times out 98 | # Used to prevent a hypothetical dead lock scenario when the pkcs#11 context needs to be reset upon HSM device failure. 99 | context_read_timeout_milli = 10 100 | 101 | #[pkcs11_logger] 102 | # https://github.com/Pkcs11Interop/pkcs11-logger 103 | # Default value for the PKCS11_LOGGER_LIBRARY_PATH environment variable that 104 | # specifies the path to the original PKCS#11 library. 105 | # PKCS11_LOGGER_LIBRARY_PATH = "/usr/safenet/lunaclient/lib/libCryptoki2_64.so" 106 | #PKCS11_LOGGER_LIBRARY_PATH = "/usr/local/lib/softhsm/libsofthsm2.so" 107 | # Default value for the PKCS11_LOGGER_LOG_FILE_PATH environment variable that 108 | # specifies the path to the log file. 109 | #PKCS11_LOGGER_LOG_FILE_PATH = "/var/local/xks-proxy/logs/pkcs11-logger-output.log" 110 | # Default value for the PKCS11_LOGGER_FLAGS environment variable that 111 | # specifies a bit mask that controls multiple logger features. 112 | #PKCS11_LOGGER_FLAGS = "0" 113 | 114 | [limits] 115 | max_plaintext_in_base64 = 8192 116 | # AAD in binary must not exceed 65,535 bytes as limited by the 2-byte AAD length requirement in the XKSProxy API spec 117 | max_aad_in_base64 = 16384 118 | 119 | [hsm_capabilities] 120 | # SoftHSMv2 doesn't know how to generate IV. 121 | # So the solution is to use SoftHSMv2 to generate random for the IV. 122 | can_generate_iv = false 123 | is_zero_iv_required = false 124 | -------------------------------------------------------------------------------- /xks-axum/configuration/settings_cloudhsm.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This is a sample xks-proxy configuration file of using CloudHSM with pkcs11-logger enabled in a Linux environment. 5 | # It assumes you have installed and set up the necessary AWS CloudHSM client side library. 6 | [server] 7 | ip = "0.0.0.0" 8 | port = 443 9 | # (Optional) port used for http ping. Defaults to 80. 10 | # port_http_ping = 80 11 | region = "us-east-2" 12 | service = "kms-xks-proxy" 13 | # Optional configuration of ciphertext metadata in base 64 encoding 14 | # ciphertext_metadata_b64 = "djAuMC4x" 15 | 16 | # Configuration of TCP keepalive probes 17 | # https://en.wikipedia.org/wiki/Keepalive 18 | [server.tcp_keepalive] 19 | # (Optional) Number of seconds between two keepalive transmissions in idle condition 20 | # No configuration means TCP keepalive probes is disabled. 21 | tcp_keepalive_secs = 60 22 | # (Optional) Number of retransmissions to be carried out before declaring that remote end is not available 23 | tcp_keepalive_retries = 3 24 | # (Optional) Number of seconds between two successive keepalive retransmissions, 25 | # if acknowledgement to the previous keepalive transmission is not received 26 | tcp_keepalive_interval_secs = 1 27 | 28 | [tracing] 29 | # Used to control logging to stdout 30 | is_stdout_writer_enabled = true 31 | # Used to control logging to a file (rotated hourly) 32 | is_file_writer_enabled = true 33 | 34 | # Supported trace levels: TRACE, DEBUG, INFO, WARN, ERROR 35 | # Should be set to INFO for production. 36 | level = "DEBUG" 37 | 38 | # Directory and file prefix, applicable only if is_file_writer_enabled = true 39 | directory = "/var/local/xks-proxy/logs" 40 | file_prefix = "xks-proxy.log" 41 | # Supported rotation: MINUTELY, HOURLY, DAILY, NEVER 42 | # Should never be set to NEVER in production. 43 | # Adjust the timer calender of xks-proxy_cleanlogs.timer via "systemctl edit xks-proxy_cleanlogs.timer" as needed. 44 | rotation_kind = "HOURLY" 45 | 46 | [security] 47 | # is_sigv4_auth_enabled must be set to true for production. 48 | is_sigv4_auth_enabled = true 49 | is_tls_enabled = true 50 | is_mtls_enabled = false 51 | 52 | # (Optional) secondary authorization used 53 | #secondary_auth = "Oso" 54 | 55 | #[security.oso] 56 | #polar_file_path = "configuration/oso.polar" 57 | 58 | [tls] 59 | # Applicable when is_tls_enabled = true 60 | tls_cert_pem = "tls/server_cert.pem" 61 | tls_key_pem = "tls/server_key.pem" 62 | # Applicable when is_mtls_enabled = true 63 | mtls_client_ca_pem = "tls/client_ca.pem" 64 | mtls_client_dns_name = "us-east-1.alpha.cks.kms.aws.internal.amazonaws.com" 65 | 66 | [[external_key_stores]] 67 | # Each uri path prefix defines a logical xks, and therefore every uri path prefix must be unique. 68 | # A Proxy URI path prefix is either empty, or it must have between 9 and 117 characters. 69 | # Valid characters are a-z, A-Z, 0-9, /, - (hyphen), and _ (underscore) 70 | uri_path_prefix = "" 71 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 72 | sigv4_access_key_id = "" 73 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 74 | sigv4_secret_access_key = "" 75 | # Each xks key id must exist in the underlying HSM with the corresponding pkcs11 label 76 | xks_key_id_set = ["foo", "cat", "dog"] 77 | 78 | [[external_key_stores]] 79 | uri_path_prefix = "/example/uri/path/prefix" 80 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 81 | sigv4_access_key_id = "" 82 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 83 | sigv4_secret_access_key = "" 84 | xks_key_id_set = ["foo", "bar"] 85 | 86 | [pkcs11] 87 | session_pool_max_size = 30 88 | session_pool_timeout_milli = 0 89 | # Set to true for testing purposes only 90 | session_eager_close = false 91 | user_pin = "xks_proxy:1234567" 92 | # Default value for the PKCS11_HSM_MODULE environment variable that 93 | # specifies the file path to the PKCS#11 library. 94 | PKCS11_HSM_MODULE = "/local/centos/pkcs11-logger/build/linux/pkcs11-logger-x64.so" 95 | # Number of milli seconds before a read access to the pkcs#11 context times out 96 | # Used to prevent a hypothetical dead lock scenario when the pkcs#11 context needs to be reset upon HSM device failure. 97 | context_read_timeout_milli = 10 98 | 99 | [pkcs11_logger] 100 | # https://github.com/Pkcs11Interop/pkcs11-logger 101 | # Default value for the PKCS11_LOGGER_LIBRARY_PATH environment variable that 102 | # specifies the path to the original PKCS#11 library. 103 | PKCS11_LOGGER_LIBRARY_PATH = "/opt/cloudhsm/lib/libcloudhsm_pkcs11.so" 104 | # Default value for the PKCS11_LOGGER_LOG_FILE_PATH environment variable that 105 | # specifies the path to the log file. 106 | PKCS11_LOGGER_LOG_FILE_PATH = "/var/local/xks-proxy/logs/pkcs11-logger-output.log" 107 | # Default value for the PKCS11_LOGGER_FLAGS environment variable that 108 | # specifies a bit mask that controls multiple logger features. 109 | PKCS11_LOGGER_FLAGS = "0" 110 | 111 | [limits] 112 | max_plaintext_in_base64 = 8192 113 | # AAD in binary must not exceed 65,535 bytes as limited by the 2-byte AAD length requirement in the XKSProxy API spec 114 | max_aad_in_base64 = 16384 115 | 116 | [hsm_capabilities] 117 | # CloudHSM always generate IV but requires the input IV to be all zeros. 118 | can_generate_iv = true 119 | is_zero_iv_required = true 120 | -------------------------------------------------------------------------------- /xks-axum/configuration/settings_docker.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This is a sample xks-proxy configuration file of using SoftHSMv2 with pkcs11-logger disabled in a Docker environment. 5 | # It assumes you have installed SoftHSMv2, and have placed its shared library at 6 | # /usr/local/lib/softhsm/libsofthsm2.so 7 | [server] 8 | ip = "0.0.0.0" 9 | port = 80 10 | # (Optional) port used for http ping. Defaults to 80. 11 | # port_http_ping = 80 12 | region = "us-east-1" 13 | service = "kms-xks-proxy" 14 | # Optional configuration of ciphertext metadata in base 64 encoding 15 | # ciphertext_metadata_b64 = "djAuMC4x" 16 | 17 | # Configuration of TCP keepalive probes 18 | # https://en.wikipedia.org/wiki/Keepalive 19 | [server.tcp_keepalive] 20 | # (Optional) Number of seconds between two keepalive transmissions in idle condition 21 | # No configuration means TCP keepalive probes is disabled. 22 | tcp_keepalive_secs = 60 23 | # (Optional) Number of retransmissions to be carried out before declaring that remote end is not available 24 | tcp_keepalive_retries = 3 25 | # (Optional) Number of seconds between two successive keepalive retransmissions, 26 | # if acknowledgement to the previous keepalive transmission is not received 27 | tcp_keepalive_interval_secs = 1 28 | 29 | [tracing] 30 | # Used to control logging to stdout 31 | is_stdout_writer_enabled = true 32 | # Used to control logging to a file (rotated hourly) 33 | is_file_writer_enabled = false 34 | 35 | # Supported trace levels: TRACE, DEBUG, INFO, WARN, ERROR 36 | # Should be set to INFO for production. 37 | level = "DEBUG" 38 | 39 | # Directory and file prefix, applicable only if is_file_writer_enabled = true 40 | # directory = "/var/local/xks-proxy/logs" 41 | # file_prefix = "xks-proxy.log" 42 | # Supported rotation: MINUTELY, HOURLY, DAILY, NEVER 43 | # Should never be set to NEVER in production. 44 | # Adjust the timer calender of xks-proxy_cleanlogs.timer via "systemctl edit xks-proxy_cleanlogs.timer" as needed. 45 | # rotation_kind = "HOURLY" 46 | 47 | [security] 48 | # is_sigv4_auth_enabled must be set to true for production. 49 | is_sigv4_auth_enabled = true 50 | is_tls_enabled = false 51 | is_mtls_enabled = false 52 | 53 | # (Optional) secondary authorization used 54 | #secondary_auth = "Oso" 55 | 56 | #[security.oso] 57 | #polar_file_path = "configuration/oso.polar" 58 | 59 | # [tls] 60 | # # Applicable when is_tls_enabled = true 61 | # tls_cert_pem = "tls/server_cert.pem" 62 | # tls_key_pem = "tls/server_key.pem" 63 | # # Applicable when is_mtls_enabled = true 64 | # mtls_client_ca_pem = "tls/client_ca.pem" 65 | # mtls_client_dns_name = "us-east-1.alpha.cks.kms.aws.internal.amazonaws.com" 66 | 67 | # [[external_key_stores]] 68 | # Each uri path prefix defines a logical xks, and therefore every uri path prefix must be unique. 69 | # A Proxy URI path prefix is either empty, or it must have between 9 and 117 characters. 70 | # Valid characters are a-z, A-Z, 0-9, /, - (hyphen), and _ (underscore) 71 | # uri_path_prefix = "" 72 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 73 | # sigv4_access_key_id = "" 74 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 75 | # sigv4_secret_access_key = "" 76 | # Each xks key id must exist in the underlying HSM with the corresponding pkcs11 label 77 | # xks_key_id_set = ["foo", "cat", "dog"] 78 | 79 | [[external_key_stores]] 80 | uri_path_prefix = "/example/uri/path/prefix" 81 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 82 | sigv4_access_key_id = "BETWEEN2TENAND3TENCHARACTERS" 83 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 84 | sigv4_secret_access_key = "PleaseReplaceThisWithSomeSecretOfLength43To64" 85 | xks_key_id_set = ["foo"] 86 | 87 | [pkcs11] 88 | session_pool_max_size = 30 89 | session_pool_timeout_milli = 0 90 | # Set to true for testing purposes only 91 | session_eager_close = false 92 | user_pin = "1234" 93 | # Default value for the PKCS11_HSM_MODULE environment variable that 94 | # specifies the file path to the PKCS#11 library. 95 | # PKCS11_HSM_MODULE = "/usr/local/lib/pkcs11-logger-x64.so" 96 | PKCS11_HSM_MODULE = "/usr/lib/softhsm/libsofthsm2.so" 97 | # Number of milli seconds before a read access to the pkcs#11 context times out 98 | # Used to prevent a hypothetical dead lock scenario when the pkcs#11 context needs to be reset upon HSM device failure. 99 | context_read_timeout_milli = 10 100 | 101 | [pkcs11_logger] 102 | # https://github.com/Pkcs11Interop/pkcs11-logger 103 | # Default value for the PKCS11_LOGGER_LIBRARY_PATH environment variable that 104 | # specifies the path to the original PKCS#11 library. 105 | PKCS11_LOGGER_LIBRARY_PATH = "" 106 | # Default value for the PKCS11_LOGGER_LOG_FILE_PATH environment variable that 107 | # specifies the path to the log file. 108 | PKCS11_LOGGER_LOG_FILE_PATH = "" 109 | # Default value for the PKCS11_LOGGER_FLAGS environment variable that 110 | # specifies a bit mask that controls multiple logger features. 111 | PKCS11_LOGGER_FLAGS = "0" 112 | 113 | [limits] 114 | max_plaintext_in_base64 = 8192 115 | # AAD in binary must not exceed 65,535 bytes as limited by the 2-byte AAD length requirement in the XKSProxy API spec 116 | max_aad_in_base64 = 16384 117 | 118 | [hsm_capabilities] 119 | # SoftHSMv2 doesn't know how to generate IV. 120 | # So the solution is to use SoftHSMv2 to generate random for the IV. 121 | can_generate_iv = false 122 | is_zero_iv_required = false 123 | -------------------------------------------------------------------------------- /xks-axum/configuration/settings_luna.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This is a sample xks-proxy configuration file of using Thales eLab with pkcs11-logger enabled in a Linux environment. 5 | # It assumes you have installed and set up the necessary Thales Luna HSM client side library. 6 | [server] 7 | ip = "0.0.0.0" 8 | port = 443 9 | # (Optional) port used for http ping. Defaults to 80. 10 | # port_http_ping = 80 11 | region = "us-east-1" 12 | service = "kms-xks-proxy" 13 | # Optional configuration of ciphertext metadata in base 64 encoding 14 | # ciphertext_metadata_b64 = "djAuMC4x" 15 | 16 | # Configuration of TCP keepalive probes 17 | # https://en.wikipedia.org/wiki/Keepalive 18 | [server.tcp_keepalive] 19 | # (Optional) Number of seconds between two keepalive transmissions in idle condition 20 | # No configuration means TCP keepalive probes is disabled. 21 | tcp_keepalive_secs = 60 22 | # (Optional) Number of retransmissions to be carried out before declaring that remote end is not available 23 | tcp_keepalive_retries = 3 24 | # (Optional) Number of seconds between two successive keepalive retransmissions, 25 | # if acknowledgement to the previous keepalive transmission is not received 26 | tcp_keepalive_interval_secs = 1 27 | 28 | [tracing] 29 | # Used to control logging to stdout 30 | is_stdout_writer_enabled = true 31 | # Used to control logging to a file (rotated hourly) 32 | is_file_writer_enabled = true 33 | 34 | # Supported trace levels: TRACE, DEBUG, INFO, WARN, ERROR 35 | # Should be set to INFO for production. 36 | level = "DEBUG" 37 | 38 | # Directory and file prefix, applicable only if is_file_writer_enabled = true 39 | directory = "/var/local/xks-proxy/logs" 40 | file_prefix = "xks-proxy.log" 41 | # Supported rotation: MINUTELY, HOURLY, DAILY, NEVER 42 | # Should never be set to NEVER in production. 43 | # Adjust the timer calender of xks-proxy_cleanlogs.timer via "systemctl edit xks-proxy_cleanlogs.timer" as needed. 44 | rotation_kind = "HOURLY" 45 | 46 | [security] 47 | # is_sigv4_auth_enabled must be set to true for production. 48 | is_sigv4_auth_enabled = true 49 | is_tls_enabled = true 50 | is_mtls_enabled = false 51 | 52 | # (Optional) secondary authorization used 53 | #secondary_auth = "Oso" 54 | 55 | #[security.oso] 56 | #polar_file_path = "configuration/oso.polar" 57 | 58 | [tls] 59 | # Applicable when is_tls_enabled = true 60 | tls_cert_pem = "tls/server_cert.pem" 61 | tls_key_pem = "tls/server_key.pem" 62 | # Applicable when is_mtls_enabled = true 63 | mtls_client_ca_pem = "tls/client_ca.pem" 64 | mtls_client_dns_name = "us-east-1.alpha.cks.kms.aws.internal.amazonaws.com" 65 | 66 | [[external_key_stores]] 67 | # Each uri path prefix defines a logical xks, and therefore every uri path prefix must be unique. 68 | # A Proxy URI path prefix is either empty, or it must have between 9 and 117 characters. 69 | # Valid characters are a-z, A-Z, 0-9, /, - (hyphen), and _ (underscore) 70 | uri_path_prefix = "" 71 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 72 | sigv4_access_key_id = "" 73 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 74 | sigv4_secret_access_key = "" 75 | # Each xks key id must exist in the underlying HSM with the corresponding pkcs11 label 76 | xks_key_id_set = ["foo", "cat", "dog"] 77 | 78 | [[external_key_stores]] 79 | uri_path_prefix = "/example/uri/path/prefix" 80 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 81 | sigv4_access_key_id = "" 82 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 83 | sigv4_secret_access_key = "" 84 | xks_key_id_set = ["foo", "bar"] 85 | 86 | [pkcs11] 87 | session_pool_max_size = 30 88 | session_pool_timeout_milli = 0 89 | # Set to true for testing purposes only 90 | session_eager_close = false 91 | user_pin = "1q@W3e$R" 92 | # Default value for the PKCS11_HSM_MODULE environment variable that 93 | # specifies the file path to the PKCS#11 library. 94 | PKCS11_HSM_MODULE = "/local/centos/pkcs11-logger/build/linux/pkcs11-logger-x64.so" 95 | # Number of milli seconds before a read access to the pkcs#11 context times out 96 | # Used to prevent a hypothetical dead lock scenario when the pkcs#11 context needs to be reset upon HSM device failure. 97 | context_read_timeout_milli = 10 98 | 99 | [pkcs11_logger] 100 | # https://github.com/Pkcs11Interop/pkcs11-logger 101 | # Default value for the PKCS11_LOGGER_LIBRARY_PATH environment variable that 102 | # specifies the path to the original PKCS#11 library. 103 | PKCS11_LOGGER_LIBRARY_PATH = "/usr/safenet/lunaclient/lib/libCryptoki2_64.so" 104 | # Default value for the PKCS11_LOGGER_LOG_FILE_PATH environment variable that 105 | # specifies the path to the log file. 106 | PKCS11_LOGGER_LOG_FILE_PATH = "/var/local/xks-proxy/logs/pkcs11-logger-output.log" 107 | # Default value for the PKCS11_LOGGER_FLAGS environment variable that 108 | # specifies a bit mask that controls multiple logger features. 109 | PKCS11_LOGGER_FLAGS = "0" 110 | 111 | [limits] 112 | max_plaintext_in_base64 = 8192 113 | # AAD in binary must not exceed 65,535 bytes as limited by the 2-byte AAD length requirement in the XKSProxy API spec 114 | max_aad_in_base64 = 16384 115 | 116 | [hsm_capabilities] 117 | # Luna HSM supports IV being explicitly specified or explicitly omitted (with null pointer). 118 | # If explicitly specified, the IV will be used. 119 | # If omitted, the IV will be generated but not returned in the mechanism parameter but appended to the ciphertext 120 | # after the authentication tag. 121 | can_generate_iv = true 122 | is_zero_iv_required = false 123 | -------------------------------------------------------------------------------- /xks-axum/configuration/settings_nshield.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This is a sample xks-proxy configuration file of using nShield in a Linux environment. 5 | # It assumes you have installed and set up the necessary nShield HSM client side library. 6 | [server] 7 | ip = "0.0.0.0" 8 | port = 80 9 | # (Optional) port used for http ping. Defaults to 80. 10 | # port_http_ping = 80 11 | region = "us-east-1" 12 | service = "kms-xks-proxy" 13 | # Optional configuration of ciphertext metadata in base 64 encoding 14 | # ciphertext_metadata_b64 = "djAuMC4x" 15 | 16 | # Configuration of TCP keepalive probes 17 | # https://en.wikipedia.org/wiki/Keepalive 18 | [server.tcp_keepalive] 19 | # (Optional) Number of seconds between two keepalive transmissions in idle condition 20 | # No configuration means TCP keepalive probes is disabled. 21 | tcp_keepalive_secs = 60 22 | # (Optional) Number of retransmissions to be carried out before declaring that remote end is not available 23 | tcp_keepalive_retries = 3 24 | # (Optional) Number of seconds between two successive keepalive retransmissions, 25 | # if acknowledgement to the previous keepalive transmission is not received 26 | tcp_keepalive_interval_secs = 1 27 | 28 | [tracing] 29 | # Used to control logging to stdout 30 | is_stdout_writer_enabled = true 31 | # Used to control logging to a file (rotated hourly) 32 | is_file_writer_enabled = true 33 | 34 | # Supported trace levels: TRACE, DEBUG, INFO, WARN, ERROR 35 | # Should be set to INFO for production. 36 | level = "DEBUG" 37 | 38 | # Directory and file prefix, applicable only if is_file_writer_enabled = true 39 | directory = "/var/local/xks-proxy/logs" 40 | file_prefix = "xks-proxy.log" 41 | # Supported rotation: MINUTELY, HOURLY, DAILY, NEVER 42 | # Should never be set to NEVER in production. 43 | # Adjust the timer calender of xks-proxy_cleanlogs.timer via "systemctl edit xks-proxy_cleanlogs.timer" as needed. 44 | rotation_kind = "HOURLY" 45 | 46 | [security] 47 | # is_sigv4_auth_enabled must be set to true for production. 48 | is_sigv4_auth_enabled = true 49 | is_tls_enabled = false 50 | is_mtls_enabled = false 51 | 52 | # (Optional) secondary authorization used 53 | #secondary_auth = "Oso" 54 | 55 | #[security.oso] 56 | #polar_file_path = "configuration/oso.polar" 57 | 58 | [tls] 59 | # Applicable when is_tls_enabled = true 60 | tls_cert_pem = "" 61 | tls_key_pem = "" 62 | # Applicable when is_mtls_enabled = true 63 | mtls_client_ca_pem = "" 64 | mtls_client_dns_name = "" 65 | 66 | [[external_key_stores]] 67 | # Each uri path prefix defines a logical xks, and therefore every uri path prefix must be unique. 68 | # A Proxy URI path prefix is either empty, or it must have between 9 and 117 characters. 69 | # Valid characters are a-z, A-Z, 0-9, /, - (hyphen), and _ (underscore) 70 | uri_path_prefix = "" 71 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 72 | sigv4_access_key_id = "" 73 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 74 | sigv4_secret_access_key = "" 75 | # Each xks key id must exist in the underlying HSM with the corresponding pkcs11 label 76 | xks_key_id_set = ["foo", "cat", "dog"] 77 | 78 | [[external_key_stores]] 79 | uri_path_prefix = "/example/uri/path/prefix" 80 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 81 | sigv4_access_key_id = "BETWEEN2TENAND3TENCHARACTERS" 82 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 83 | sigv4_secret_access_key = "PleaseReplaceThisWithSomeSecretOfLength43To64" 84 | xks_key_id_set = ["foo", "bar"] 85 | 86 | [pkcs11] 87 | session_pool_max_size = 30 88 | session_pool_timeout_milli = 0 89 | # Set to true for testing purposes only 90 | session_eager_close = false 91 | user_pin = "replaceme" 92 | # Default value for the PKCS11_HSM_MODULE environment variable that 93 | # specifies the file path to the PKCS#11 library. 94 | # PKCS11_HSM_MODULE = "/local/centos/pkcs11-logger/build/linux/pkcs11-logger-x64.so" 95 | PKCS11_HSM_MODULE = "/opt/nfast/toolkits/pkcs11/libcknfast.so" 96 | # Number of milli seconds before a read access to the pkcs#11 context times out 97 | # Used to prevent a hypothetical dead lock scenario when the pkcs#11 context needs to be reset upon HSM device failure. 98 | context_read_timeout_milli = 10 99 | 100 | # [pkcs11_logger] 101 | # https://github.com/Pkcs11Interop/pkcs11-logger 102 | # Default value for the PKCS11_LOGGER_LIBRARY_PATH environment variable that 103 | # specifies the path to the original PKCS#11 library. 104 | # PKCS11_LOGGER_LIBRARY_PATH = "/opt/nfast/toolkits/pkcs11/libcknfast.so" 105 | # Default value for the PKCS11_LOGGER_LOG_FILE_PATH environment variable that 106 | # specifies the path to the log file. 107 | # PKCS11_LOGGER_LOG_FILE_PATH = "/var/local/xks-proxy/logs/pkcs11-logger-output.log" 108 | # Default value for the PKCS11_LOGGER_FLAGS environment variable that 109 | # specifies a bit mask that controls multiple logger features. 110 | # PKCS11_LOGGER_FLAGS = "0" 111 | 112 | [limits] 113 | max_plaintext_in_base64 = 8192 114 | # AAD in binary must not exceed 65,535 bytes as limited by the 2-byte AAD length requirement in the XKSProxy API spec 115 | max_aad_in_base64 = 16384 116 | 117 | [hsm_capabilities] 118 | # nShield HSM requires to generate the IV. 119 | can_generate_iv = true 120 | is_zero_iv_required = true 121 | -------------------------------------------------------------------------------- /xks-axum/configuration/settings_softhsmv2.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This is a sample xks-proxy configuration file of using SoftHSMv2 with pkcs11-logger enabled in a Linux environment. 5 | # It assumes you have installed SoftHSMv2, and have placed its shared library at 6 | # /usr/local/lib/softhsm/libsofthsm2.so 7 | [server] 8 | ip = "0.0.0.0" 9 | port = 80 10 | # (Optional) port used for http ping. Defaults to 80. 11 | # port_http_ping = 80 12 | region = "us-east-1" 13 | service = "kms-xks-proxy" 14 | # Optional configuration of ciphertext metadata in base 64 encoding 15 | # ciphertext_metadata_b64 = "djAuMC4x" 16 | 17 | # Configuration of TCP keepalive probes 18 | # https://en.wikipedia.org/wiki/Keepalive 19 | [server.tcp_keepalive] 20 | # (Optional) Number of seconds between two keepalive transmissions in idle condition 21 | # No configuration means TCP keepalive probes is disabled. 22 | tcp_keepalive_secs = 60 23 | # (Optional) Number of retransmissions to be carried out before declaring that remote end is not available 24 | tcp_keepalive_retries = 3 25 | # (Optional) Number of seconds between two successive keepalive retransmissions, 26 | # if acknowledgement to the previous keepalive transmission is not received 27 | tcp_keepalive_interval_secs = 1 28 | 29 | [tracing] 30 | # Used to control logging to stdout 31 | is_stdout_writer_enabled = true 32 | # Used to control logging to a file (rotated hourly) 33 | is_file_writer_enabled = true 34 | 35 | # Supported trace levels: TRACE, DEBUG, INFO, WARN, ERROR 36 | # Should be set to INFO for production. 37 | level = "DEBUG" 38 | 39 | # Directory and file prefix, applicable only if is_file_writer_enabled = true 40 | directory = "/var/local/xks-proxy/logs" 41 | file_prefix = "xks-proxy.log" 42 | # Supported rotation: MINUTELY, HOURLY, DAILY, NEVER 43 | # Should never be set to NEVER in production. 44 | # Adjust the timer calender of xks-proxy_cleanlogs.timer via "systemctl edit xks-proxy_cleanlogs.timer" as needed. 45 | rotation_kind = "HOURLY" 46 | 47 | [security] 48 | # is_sigv4_auth_enabled must be set to true for production. 49 | is_sigv4_auth_enabled = true 50 | is_tls_enabled = false 51 | is_mtls_enabled = false 52 | 53 | # (Optional) secondary authorization used 54 | #secondary_auth = "Oso" 55 | 56 | #[security.oso] 57 | #polar_file_path = "configuration/oso.polar" 58 | 59 | [tls] 60 | # Applicable when is_tls_enabled = true 61 | tls_cert_pem = "tls/server_cert.pem" 62 | tls_key_pem = "tls/server_key.pem" 63 | # Applicable when is_mtls_enabled = true 64 | mtls_client_ca_pem = "tls/client_ca.pem" 65 | mtls_client_dns_name = "us-east-1.alpha.cks.kms.aws.internal.amazonaws.com" 66 | 67 | [[external_key_stores]] 68 | # Each uri path prefix defines a logical xks, and therefore every uri path prefix must be unique. 69 | # A Proxy URI path prefix is either empty, or it must have between 9 and 117 characters. 70 | # Valid characters are a-z, A-Z, 0-9, /, - (hyphen), and _ (underscore) 71 | uri_path_prefix = "" 72 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 73 | sigv4_access_key_id = "" 74 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 75 | sigv4_secret_access_key = "" 76 | # Each xks key id must exist in the underlying HSM with the corresponding pkcs11 label 77 | xks_key_id_set = ["foo", "cat", "dog"] 78 | 79 | [[external_key_stores]] 80 | uri_path_prefix = "/example/uri/path/prefix" 81 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 82 | sigv4_access_key_id = "" 83 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 84 | sigv4_secret_access_key = "" 85 | xks_key_id_set = ["foo", "bar"] 86 | 87 | [pkcs11] 88 | session_pool_max_size = 30 89 | session_pool_timeout_milli = 0 90 | # Set to true for testing purposes only 91 | session_eager_close = false 92 | user_pin = "1234" 93 | # Default value for the PKCS11_HSM_MODULE environment variable that 94 | # specifies the file path to the PKCS#11 library. 95 | PKCS11_HSM_MODULE = "/local/centos/pkcs11-logger/build/linux/pkcs11-logger-x64.so" 96 | # Number of milli seconds before a read access to the pkcs#11 context times out 97 | # Used to prevent a hypothetical dead lock scenario when the pkcs#11 context needs to be reset upon HSM device failure. 98 | context_read_timeout_milli = 10 99 | 100 | [pkcs11_logger] 101 | # https://github.com/Pkcs11Interop/pkcs11-logger 102 | # Default value for the PKCS11_LOGGER_LIBRARY_PATH environment variable that 103 | # specifies the path to the original PKCS#11 library. 104 | # PKCS11_LOGGER_LIBRARY_PATH = "/usr/safenet/lunaclient/lib/libCryptoki2_64.so" 105 | PKCS11_LOGGER_LIBRARY_PATH = "/usr/local/lib/softhsm/libsofthsm2.so" 106 | # Default value for the PKCS11_LOGGER_LOG_FILE_PATH environment variable that 107 | # specifies the path to the log file. 108 | PKCS11_LOGGER_LOG_FILE_PATH = "/var/local/xks-proxy/logs/pkcs11-logger-output.log" 109 | # Default value for the PKCS11_LOGGER_FLAGS environment variable that 110 | # specifies a bit mask that controls multiple logger features. 111 | PKCS11_LOGGER_FLAGS = "0" 112 | 113 | [limits] 114 | max_plaintext_in_base64 = 8192 115 | # AAD in binary must not exceed 65,535 bytes as limited by the 2-byte AAD length requirement in the XKSProxy API spec 116 | max_aad_in_base64 = 16384 117 | 118 | [hsm_capabilities] 119 | # SoftHSMv2 doesn't know how to generate IV. 120 | # So the solution is to use SoftHSMv2 to generate random for the IV. 121 | can_generate_iv = false 122 | is_zero_iv_required = false 123 | -------------------------------------------------------------------------------- /xks-axum/configuration/settings_softhsmv2_osx.toml: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This is a sample xks-proxy configuration file of using SoftHSMv2 with pkcs11-logger enabled in an OSX envrionment. 5 | # It assumes you have 6 | # 1. installed SoftHSMv2, and have placed its shared library at /usr/local/lib/softhsm/libsofthsm2.so; and 7 | # 2. installed pkcs11-logger, and have placed its OSX dynamic library at /usr/local/lib/pkcs11-logger-x64.dylib 8 | [server] 9 | ip = "0.0.0.0" 10 | port = 80 11 | # (Optional) port used for http ping. Defaults to 80. 12 | # port_http_ping = 80 13 | region = "us-east-1" 14 | service = "kms-xks-proxy" 15 | # Optional configuration of ciphertext metadata in base 64 encoding 16 | # ciphertext_metadata_b64 = "djAuMC4x" 17 | 18 | # Configuration of TCP keepalive probes 19 | # https://en.wikipedia.org/wiki/Keepalive 20 | [server.tcp_keepalive] 21 | # (Optional) Number of seconds between two keepalive transmissions in idle condition 22 | # No configuration means TCP keepalive probes is disabled. 23 | tcp_keepalive_secs = 60 24 | # (Optional) Number of retransmissions to be carried out before declaring that remote end is not available 25 | tcp_keepalive_retries = 3 26 | # (Optional) Number of seconds between two successive keepalive retransmissions, 27 | # if acknowledgement to the previous keepalive transmission is not received 28 | tcp_keepalive_interval_secs = 1 29 | 30 | [tracing] 31 | # Used to control logging to stdout 32 | is_stdout_writer_enabled = true 33 | # Used to control logging to a file (rotated hourly) 34 | is_file_writer_enabled = true 35 | 36 | # Supported trace levels: TRACE, DEBUG, INFO, WARN, ERROR 37 | # Should be set to INFO for production. 38 | level = "DEBUG" 39 | 40 | # Directory and file prefix, applicable only if is_file_writer_enabled = true 41 | directory = "/var/local/xks-proxy/logs" 42 | file_prefix = "xks-proxy.log" 43 | # Supported rotation: MINUTELY, HOURLY, DAILY, NEVER 44 | # Should never be set to NEVER in production. 45 | # Adjust the timer calender of xks-proxy_cleanlogs.timer via "systemctl edit xks-proxy_cleanlogs.timer" as needed. 46 | rotation_kind = "HOURLY" 47 | 48 | [security] 49 | # is_sigv4_auth_enabled must be set to true for production. 50 | is_sigv4_auth_enabled = true 51 | is_tls_enabled = false 52 | is_mtls_enabled = false 53 | 54 | # (Optional) secondary authorization used 55 | # secondary_auth = "Oso" 56 | 57 | # [security.oso] 58 | # polar_file_path = "configuration/oso.polar" 59 | 60 | [tls] 61 | # Applicable when is_tls_enabled = true 62 | tls_cert_pem = "tls/server_cert.pem" 63 | tls_key_pem = "tls/server_key.pem" 64 | # Applicable when is_mtls_enabled = true 65 | mtls_client_ca_pem = "tls/client_ca.pem" 66 | mtls_client_dns_name = "us-east-1.alpha.cks.kms.aws.internal.amazonaws.com" 67 | 68 | [[external_key_stores]] 69 | # Each uri path prefix defines a logical xks, and therefore every uri path prefix must be unique. 70 | # A Proxy URI path prefix is either empty, or it must have between 9 and 117 characters. 71 | # Valid characters are a-z, A-Z, 0-9, /, - (hyphen), and _ (underscore) 72 | uri_path_prefix = "" 73 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 74 | sigv4_access_key_id = "" 75 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 76 | sigv4_secret_access_key = "" 77 | # Each xks key id must exist in the underlying HSM with the corresponding pkcs11 label 78 | xks_key_id_set = ["foo", "cat", "dog"] 79 | 80 | [[external_key_stores]] 81 | uri_path_prefix = "/example/uri/path/prefix" 82 | # Access key ID must have between 20 and 30 characters. Valid characters are uppercase A-Z and 2-7 83 | sigv4_access_key_id = "" 84 | # Secret access key must have between 43 and 64 characters. Valid characters are a-z, A-Z, 0-9, /, +, and = 85 | sigv4_secret_access_key = "" 86 | xks_key_id_set = ["foo", "bar"] 87 | 88 | [pkcs11] 89 | session_pool_max_size = 30 90 | session_pool_timeout_milli = 0 91 | # Set to true for testing purposes only 92 | session_eager_close = false 93 | user_pin = "1234" 94 | # Default value for the PKCS11_HSM_MODULE environment variable that 95 | # specifies the file path to the PKCS#11 library. 96 | # On OSX, you can "cp lib/pkcs11-logger-x64.dylib /usr/local/lib/" to set this up. 97 | PKCS11_HSM_MODULE = "/usr/local/lib/pkcs11-logger-x64.dylib" 98 | # Number of milli seconds before a read access to the pkcs#11 context times out 99 | # Used to prevent a hypothetical dead lock scenario when the pkcs#11 context needs to be reset upon HSM device failure. 100 | context_read_timeout_milli = 10 101 | 102 | [pkcs11_logger] 103 | # https://github.com/Pkcs11Interop/pkcs11-logger 104 | # Default value for the PKCS11_LOGGER_LIBRARY_PATH environment variable that 105 | # specifies the path to the original PKCS#11 library. 106 | # PKCS11_LOGGER_LIBRARY_PATH = "/usr/safenet/lunaclient/lib/libCryptoki2_64.so" 107 | PKCS11_LOGGER_LIBRARY_PATH = "/usr/local/lib/softhsm/libsofthsm2.so" 108 | # Default value for the PKCS11_LOGGER_LOG_FILE_PATH environment variable that 109 | # specifies the path to the log file. 110 | PKCS11_LOGGER_LOG_FILE_PATH = "/var/local/xks-proxy/logs/pkcs11-logger-output.log" 111 | # Default value for the PKCS11_LOGGER_FLAGS environment variable that 112 | # specifies a bit mask that controls multiple logger features. 113 | PKCS11_LOGGER_FLAGS = "0" 114 | 115 | [limits] 116 | max_plaintext_in_base64 = 8192 117 | # AAD in binary must not exceed 65,535 bytes as limited by the 2-byte AAD length requirement in the XKSProxy API spec 118 | max_aad_in_base64 = 16384 119 | 120 | [hsm_capabilities] 121 | # SoftHSMv2 doesn't know how to generate IV. 122 | # So the solution is to use SoftHSMv2 to generate random for the IV. 123 | can_generate_iv = false 124 | is_zero_iv_required = false 125 | -------------------------------------------------------------------------------- /xks-axum/osx/README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ## Building pkcs11-logger module on Mac OSX 7 | 8 | If you want to make use of the `pkcs11-logger module` for testing on Mac OSX, you can build it from source by git cloning the [pkcs11-logger git repository](https://github.com/Pkcs11Interop/pkcs11-logger.git) and follow the [build instructions for Mac OSX](https://github.com/Pkcs11Interop/pkcs11-logger#mac-os-x). 9 | 10 | If you ran into build problems related to [32-bits support being removed by Apple](https://github.com/Pkcs11Interop/pkcs11-logger/issues/7), you can try applying [this pull request](https://github.com/Pkcs11Interop/pkcs11-logger/pull/8). 11 | 12 | Alternatively, you can git clone [this git repository](https://github.com/hansonchar/pkcs11-logger.git) which has a "pull-8" branch that already has the above pull request applied. 13 | -------------------------------------------------------------------------------- /xks-axum/rust-pkcs11/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Marcus Heese 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | [package] 15 | name = "pkcs11" 16 | version = "0.5.0" 17 | authors = ["Marcus Heese "] 18 | description = "Rust PKCS#11 Library" 19 | #documentation = "https://github.com/mheese/rust-pkcs11" 20 | homepage = "https://github.com/mheese/rust-pkcs11" 21 | repository = "https://github.com/mheese/rust-pkcs11" 22 | readme = "README.md" 23 | keywords = ["pkcs11", "cryptoki"] 24 | categories = ["external-ffi-bindings", "cryptography", "hardware-support"] 25 | license = "Apache-2.0" 26 | license-file = "LICENSE" 27 | exclude = [ 28 | "pkcs11-docs/**", 29 | ] 30 | 31 | [badges] 32 | maintenance = { status = "actively-developed" } 33 | codecov = { repository = "mheese/rust-pkcs11", branch = "master", service = "github" } 34 | is-it-maintained-issue-resolution = { repository = "mheese/rust-pkcs11" } 35 | is-it-maintained-open-issues = { repository = "mheese/rust-pkcs11" } 36 | 37 | [dependencies] 38 | libloading = "^0.5" 39 | num-bigint = "^0.2" 40 | #libc = "0.2.33" 41 | 42 | [dev-dependencies] 43 | num-traits = "^0.1" 44 | hex = "^0.3" 45 | serial_test = "~0.1" 46 | serial_test_derive = "~0.1" 47 | -------------------------------------------------------------------------------- /xks-axum/rust-pkcs11/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /xks-axum/rust-pkcs11/NOTICE: -------------------------------------------------------------------------------- 1 | Rust PKCS#11 Library 2 | Copyright 2017 Marcus Heese 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | 7 | ------------------------------------------------------------------------------- 8 | This product bundles OASIS PKCS#11 documentation and header files, which are 9 | available under the OASIS IPR Policy 10 | 11 | [http://www.oasis-open.org/policies-guidelines/ipr]. 12 | 13 | For details, see pkcs11-docs/. -------------------------------------------------------------------------------- /xks-axum/rust-pkcs11/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Rust PKCS#11 Library 18 | 19 | [![Latest version](https://img.shields.io/crates/v/pkcs11.svg)](https://crates.io/crates/pkcs11) 20 | [![Documentation](https://docs.rs/pkcs11/badge.svg)](https://docs.rs/pkcs11) 21 | ![Build status](https://github.com/mheese/rust-pkcs11/workflows/Linux/badge.svg) 22 | ![Build status](https://github.com/mheese/rust-pkcs11/workflows/macOS/badge.svg) 23 | ![Build status](https://github.com/mheese/rust-pkcs11/workflows/Windows/badge.svg) 24 | ![Build status](https://github.com/mheese/rust-pkcs11/workflows/Audit/badge.svg) 25 | [![codecov](https://codecov.io/gh/mheese/rust-pkcs11/branch/master/graph/badge.svg)](https://codecov.io/gh/mheese/rust-pkcs11) 26 | ![License](https://img.shields.io/crates/l/pkcs11.svg) 27 | 28 | This is a library which brings support for PKCS#11 to Rust. It is aiming at having both a very low-level API to map the PKCS#11 functionality to Rust as well as having a higher-level API for more easy usage as well as bringing more safety for programming against PKCS#11. 29 | 30 | ## Status 31 | 32 | The library has full support for all functions in PKCS#11 v2.40. 33 | It should technically work with any Cryptoki version from v2.00. 34 | For example there is special handling for `C_WaitForSlotEvent` which has been added only in v2.01. 35 | You can successfully implement and reach all low-level Cryptoki semantics and structures. 36 | All of them are integration tested using SoftHSM. 37 | For better interoperability the low-level API is using nearly the same function/method calls and data structures as defined in the official standard. 38 | That means that using the low-level API should be very easy for people who are familiar with PKCS#11 as the naming and variables/constants/defines are the same. 39 | 40 | A high-level more Rust-friendly API is in the design process. 41 | Its goal is to hide most of the low-level PKCS#11 semantics that one does not need to be aware of as they can be very verbose. 42 | Furthermore using Rust datastructures it is possible to come up with a more type-safe library at compile time to help users to use PKCS#11 more successfully and to make it more robust. 43 | It will also provide easier primitives for multi-part encrypting/decrypting/signing/etc. 44 | Ideally by providing a streaming API. 45 | Last but not least it will provide session management and lock/unlock free sessions as they are available from the context. 46 | Especially on tokens that provide parallel processing this can be a very tedious and error-prone process. 47 | 48 | ## Compatiblity Matrix 49 | 50 | **TODO:** This is still in the making, and most likely very incomplete. 51 | 52 | As PKCS#11 implementations are not always sticking to the standard, your token might still have problems, unfortunately. 53 | These are known tokens as reported by users that definitely work together with this library. 54 | 55 | - [SoftHSM version 2](https://github.com/opendnssec/SoftHSMv2) (duh, who would have thought) 56 | - [Nitrokey HSM 2](https://www.nitrokey.com) 57 | - [CardConnect SmartCard-HSM](https://www.smartcard-hsm.com/) 58 | - Safenet iKey 2032 59 | - and probably a lot more... 60 | 61 | If you use this library with an HSM that is not listed here, please open an issue (or even better a PR) so that I can update this matrix. 62 | If your token does not work, please also open an issue, of course, so that we can investigate. 63 | 64 | ## Testing 65 | 66 | Testing is currently done with [SoftHSM2](https://github.com/opendnssec/SoftHSMv2 "SoftHSM2 Repo"). 67 | A trillion thanks to the people at OpenDNSSEC for writing SoftHSM. 68 | This makes it possible to develop applications that need to support PKCS#11. 69 | I would have no idea what to do without it. 70 | (Suggestions are always welcome.) 71 | 72 | ## TODO 73 | 74 | Here is a list of the implementation status and plans on what to do next: 75 | 76 | - [x] Dynamic loading of PKCS#11 module (thanks to [libloading](https://github.com/nagisa/rust_libloading "libloading Repo")) 77 | - [x] Initializing and Dropping PKCS#11 context 78 | - [x] Implementing Token and PIN Management functions 79 | - [x] Implementing Session Management functions 80 | - [x] Implementing Object Management functions 81 | - [x] Implementing Key Management functions 82 | - [x] Implementing Encryption/Decryption functions 83 | - [x] Implementing Message Digest functions 84 | - [x] Implementing Signing and MACing 85 | - [x] Implementing Verifying of signatures and MACs 86 | - [x] Implementing Dual-function cryptographic operations 87 | - [x] Implementing Legacy PKCS#11 functions 88 | - [x] Reorganize code of low-level API (too bloated, which we all know is what PKCS#11 is like) 89 | - [x] Import the rest of the C header `pkcs11t.h` types into rust 90 | - [x] Import the rest of the C header `pkcs11f.h` functions into rust 91 | - [x] Publish on crates.io (wow, that was easy) 92 | - [ ] C type constants to string converter functions, and the reverse (maybe part of the high-level API?) 93 | - [ ] Design and implement high-level API 94 | - [ ] Write and Generate Documentation for Rust docs 95 | - [ ] Better Testing (lots of repetitive code + we need a testing framework and different SoftHSM versions for different platforms) 96 | - [ ] Suppport for PKCS#11 v3.00 97 | - [ ] make packed struct and CK_ULONG / CK_LONG feature flags with platform defaults when it becomes possible - currently the default when the target is Windows as PKCS#11 explicitly demands packed structs on Windows and `unsigned long` and `long` are both only 32bit on Microsoft compilers by default. However, on any other unix platform the defaults are not really defined and one might need to opt in for one or the other. 98 | -------------------------------------------------------------------------------- /xks-axum/rust-pkcs11/pkcs11-docs/README.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Collection of PKCS#11 Documentation 18 | 19 | The purpose of this section is to provide all possible available documentation that makes programming against PKCS#11 easier. 20 | 21 | ## Collection of all PKCS#11 standards 22 | 23 | I ran across this only recently (April 2020). 24 | It is an invaluable collection of all PKCS#11 standards including all their C header files: 25 | 26 | Special thanks to the guys from [Pkcs11Interop](https://pkcs11interop.net/) to provide this collection. 27 | 28 | ## Reference Documentation 29 | 30 | When one first starts to explore the PKCS#11 world, there will be obstacles. The biggest one is to know and discover what documentation to read so that one can get familiar with the standard. It is best to read up on it in the following order: 31 | 32 | 1. [Usage Guide](http://docs.oasis-open.org/pkcs11/pkcs11-ug/v2.40/pkcs11-ug-v2.40.html "PKCS#11 v2.40 Usage Guide") - I discovered this unfortunately way too late for some reason. Giving this a read - before doing anything else - one gets actually a really good architectural overview of the standard. 33 | 2. [Base Specification](http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html "PKCS#11 v2.40 Base Specification") - This is the most important document for implementation of this library - in particular for the low-level API. It explains in detail how the C interface is structured and how to interact with it. Following this will provide all the details on how to write the FFI wrapper. 34 | 3. [Current Mechanisms](http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/pkcs11-curr-v2.40.html "PKCS#11 v2.40 Current Mechanisms") - This document is in particular important for our higher-leval API. It explains in detail on how to interface in detail with all the cryptographic algorithms. E.g. go here if you need help on how to generate and use an RSA key or how to use the token for digital signatures. 35 | 36 | ## C Header Files 37 | 38 | The C header files for PKCS #11 v2.40 can be found at this location: 39 | 40 | - 41 | - 42 | - 43 | -------------------------------------------------------------------------------- /xks-axum/rust-pkcs11/src/errors.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Marcus Heese 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std; 16 | use types::*; 17 | 18 | #[derive(Debug)] 19 | pub enum Error { 20 | Io(std::io::Error), 21 | Module(&'static str), 22 | InvalidInput(&'static str), 23 | Pkcs11(CK_RV), 24 | /// This error happens when trying to get an attribute's value which is unavailable, because the 25 | /// constant `CK_UNAVAILABLE_INFORMATION` is set in the `ulValueLen` attribute field 26 | UnavailableInformation, 27 | } 28 | 29 | impl From for Error { 30 | fn from(err: std::io::Error) -> Error { 31 | Error::Io(err) 32 | } 33 | } 34 | 35 | impl std::fmt::Display for Error { 36 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 37 | match *self { 38 | Error::Io(ref err) => write!(f, "IO: {}", err), 39 | Error::Module(ref err) => write!(f, "PKCS#11 Module: {}", err), 40 | Error::InvalidInput(ref err) => write!(f, "PKCS#11 Invalid Input: {}", err), 41 | Error::Pkcs11(ref err) => write!(f, "PKCS#11: {} (0x{:x})", strerror(*err), err), 42 | Error::UnavailableInformation => write!(f, "Attribute value is unavailable"), 43 | } 44 | } 45 | } 46 | 47 | impl std::error::Error for Error { 48 | fn cause(&self) -> Option<&dyn std::error::Error> { 49 | if let Error::Io(ref err) = self { 50 | Some(err) 51 | } else { 52 | None 53 | } 54 | } 55 | } 56 | 57 | fn strerror(err: CK_RV) -> &'static str { 58 | match err { 59 | CKR_OK => "CKR_OK", 60 | CKR_CANCEL => "CKR_CANCEL", 61 | CKR_HOST_MEMORY => "CKR_HOST_MEMORY", 62 | CKR_SLOT_ID_INVALID => "CKR_SLOT_ID_INVALID", 63 | CKR_GENERAL_ERROR => "CKR_GENERAL_ERROR", 64 | CKR_FUNCTION_FAILED => "CKR_FUNCTION_FAILED", 65 | CKR_ARGUMENTS_BAD => "CKR_ARGUMENTS_BAD", 66 | CKR_NO_EVENT => "CKR_NO_EVENT", 67 | CKR_NEED_TO_CREATE_THREADS => "CKR_NEED_TO_CREATE_THREADS", 68 | CKR_CANT_LOCK => "CKR_CANT_LOCK", 69 | CKR_ATTRIBUTE_READ_ONLY => "CKR_ATTRIBUTE_READ_ONLY", 70 | CKR_ATTRIBUTE_SENSITIVE => "CKR_ATTRIBUTE_SENSITIVE", 71 | CKR_ATTRIBUTE_TYPE_INVALID => "CKR_ATTRIBUTE_TYPE_INVALID", 72 | CKR_ATTRIBUTE_VALUE_INVALID => "CKR_ATTRIBUTE_VALUE_INVALID", 73 | CKR_ACTION_PROHIBITED => "CKR_ACTION_PROHIBITED", 74 | CKR_DATA_INVALID => "CKR_DATA_INVALID", 75 | CKR_DATA_LEN_RANGE => "CKR_DATA_LEN_RANGE", 76 | CKR_DEVICE_ERROR => "CKR_DEVICE_ERROR", 77 | CKR_DEVICE_MEMORY => "CKR_DEVICE_MEMORY", 78 | CKR_DEVICE_REMOVED => "CKR_DEVICE_REMOVED", 79 | CKR_ENCRYPTED_DATA_INVALID => "CKR_ENCRYPTED_DATA_INVALID", 80 | CKR_ENCRYPTED_DATA_LEN_RANGE => "CKR_ENCRYPTED_DATA_LEN_RANGE", 81 | CKR_FUNCTION_CANCELED => "CKR_FUNCTION_CANCELED", 82 | CKR_FUNCTION_NOT_PARALLEL => "CKR_FUNCTION_NOT_PARALLEL", 83 | CKR_FUNCTION_NOT_SUPPORTED => "CKR_FUNCTION_NOT_SUPPORTED", 84 | CKR_KEY_HANDLE_INVALID => "CKR_KEY_HANDLE_INVALID", 85 | CKR_KEY_SIZE_RANGE => "CKR_KEY_SIZE_RANGE", 86 | CKR_KEY_TYPE_INCONSISTENT => "CKR_KEY_TYPE_INCONSISTENT", 87 | CKR_KEY_NOT_NEEDED => "CKR_KEY_NOT_NEEDED", 88 | CKR_KEY_CHANGED => "CKR_KEY_CHANGED", 89 | CKR_KEY_NEEDED => "CKR_KEY_NEEDED", 90 | CKR_KEY_INDIGESTIBLE => "CKR_KEY_INDIGESTIBLE", 91 | CKR_KEY_FUNCTION_NOT_PERMITTED => "CKR_KEY_FUNCTION_NOT_PERMITTED", 92 | CKR_KEY_NOT_WRAPPABLE => "CKR_KEY_NOT_WRAPPABLE", 93 | CKR_KEY_UNEXTRACTABLE => "CKR_KEY_UNEXTRACTABLE", 94 | CKR_MECHANISM_INVALID => "CKR_MECHANISM_INVALID", 95 | CKR_MECHANISM_PARAM_INVALID => "CKR_MECHANISM_PARAM_INVALID", 96 | CKR_OBJECT_HANDLE_INVALID => "CKR_OBJECT_HANDLE_INVALID", 97 | CKR_OPERATION_ACTIVE => "CKR_OPERATION_ACTIVE", 98 | CKR_OPERATION_NOT_INITIALIZED => "CKR_OPERATION_NOT_INITIALIZED", 99 | CKR_PIN_INCORRECT => "CKR_PIN_INCORRECT", 100 | CKR_PIN_INVALID => "CKR_PIN_INVALID", 101 | CKR_PIN_LEN_RANGE => "CKR_PIN_LEN_RANGE", 102 | CKR_PIN_EXPIRED => "CKR_PIN_EXPIRED", 103 | CKR_PIN_LOCKED => "CKR_PIN_LOCKED", 104 | CKR_SESSION_CLOSED => "CKR_SESSION_CLOSED", 105 | CKR_SESSION_COUNT => "CKR_SESSION_COUNT", 106 | CKR_SESSION_HANDLE_INVALID => "CKR_SESSION_HANDLE_INVALID", 107 | CKR_SESSION_PARALLEL_NOT_SUPPORTED => "CKR_SESSION_PARALLEL_NOT_SUPPORTED", 108 | CKR_SESSION_READ_ONLY => "CKR_SESSION_READ_ONLY", 109 | CKR_SESSION_EXISTS => "CKR_SESSION_EXISTS", 110 | CKR_SESSION_READ_ONLY_EXISTS => "CKR_SESSION_READ_ONLY_EXISTS", 111 | CKR_SESSION_READ_WRITE_SO_EXISTS => "CKR_SESSION_READ_WRITE_SO_EXISTS", 112 | CKR_SIGNATURE_INVALID => "CKR_SIGNATURE_INVALID", 113 | CKR_SIGNATURE_LEN_RANGE => "CKR_SIGNATURE_LEN_RANGE", 114 | CKR_TEMPLATE_INCOMPLETE => "CKR_TEMPLATE_INCOMPLETE", 115 | CKR_TEMPLATE_INCONSISTENT => "CKR_TEMPLATE_INCONSISTENT", 116 | CKR_TOKEN_NOT_PRESENT => "CKR_TOKEN_NOT_PRESENT", 117 | CKR_TOKEN_NOT_RECOGNIZED => "CKR_TOKEN_NOT_RECOGNIZED", 118 | CKR_TOKEN_WRITE_PROTECTED => "CKR_TOKEN_WRITE_PROTECTED", 119 | CKR_UNWRAPPING_KEY_HANDLE_INVALID => "CKR_UNWRAPPING_KEY_HANDLE_INVALID", 120 | CKR_UNWRAPPING_KEY_SIZE_RANGE => "CKR_UNWRAPPING_KEY_SIZE_RANGE", 121 | CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT => "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT", 122 | CKR_USER_ALREADY_LOGGED_IN => "CKR_USER_ALREADY_LOGGED_IN", 123 | CKR_USER_NOT_LOGGED_IN => "CKR_USER_NOT_LOGGED_IN", 124 | CKR_USER_PIN_NOT_INITIALIZED => "CKR_USER_PIN_NOT_INITIALIZED", 125 | CKR_USER_TYPE_INVALID => "CKR_USER_TYPE_INVALID", 126 | CKR_USER_ANOTHER_ALREADY_LOGGED_IN => "CKR_USER_ANOTHER_ALREADY_LOGGED_IN", 127 | CKR_USER_TOO_MANY_TYPES => "CKR_USER_TOO_MANY_TYPES", 128 | CKR_WRAPPED_KEY_INVALID => "CKR_WRAPPED_KEY_INVALID", 129 | CKR_WRAPPED_KEY_LEN_RANGE => "CKR_WRAPPED_KEY_LEN_RANGE", 130 | CKR_WRAPPING_KEY_HANDLE_INVALID => "CKR_WRAPPING_KEY_HANDLE_INVALID", 131 | CKR_WRAPPING_KEY_SIZE_RANGE => "CKR_WRAPPING_KEY_SIZE_RANGE", 132 | CKR_WRAPPING_KEY_TYPE_INCONSISTENT => "CKR_WRAPPING_KEY_TYPE_INCONSISTENT", 133 | CKR_RANDOM_SEED_NOT_SUPPORTED => "CKR_RANDOM_SEED_NOT_SUPPORTED", 134 | CKR_RANDOM_NO_RNG => "CKR_RANDOM_NO_RNG", 135 | CKR_DOMAIN_PARAMS_INVALID => "CKR_DOMAIN_PARAMS_INVALID", 136 | CKR_CURVE_NOT_SUPPORTED => "CKR_CURVE_NOT_SUPPORTED", 137 | CKR_BUFFER_TOO_SMALL => "CKR_BUFFER_TOO_SMALL", 138 | CKR_SAVED_STATE_INVALID => "CKR_SAVED_STATE_INVALID", 139 | CKR_INFORMATION_SENSITIVE => "CKR_INFORMATION_SENSITIVE", 140 | CKR_STATE_UNSAVEABLE => "CKR_STATE_UNSAVEABLE", 141 | CKR_CRYPTOKI_NOT_INITIALIZED => "CKR_CRYPTOKI_NOT_INITIALIZED", 142 | CKR_CRYPTOKI_ALREADY_INITIALIZED => "CKR_CRYPTOKI_ALREADY_INITIALIZED", 143 | CKR_MUTEX_BAD => "CKR_MUTEX_BAD", 144 | CKR_MUTEX_NOT_LOCKED => "CKR_MUTEX_NOT_LOCKED", 145 | CKR_NEW_PIN_MODE => "CKR_NEW_PIN_MODE", 146 | CKR_NEXT_OTP => "CKR_NEXT_OTP", 147 | CKR_EXCEEDED_MAX_ITERATIONS => "CKR_EXCEEDED_MAX_ITERATIONS", 148 | CKR_FIPS_SELF_TEST_FAILED => "CKR_FIPS_SELF_TEST_FAILED", 149 | CKR_LIBRARY_LOAD_FAILED => "CKR_LIBRARY_LOAD_FAILED", 150 | CKR_PIN_TOO_WEAK => "CKR_PIN_TOO_WEAK", 151 | CKR_PUBLIC_KEY_INVALID => "CKR_PUBLIC_KEY_INVALID", 152 | CKR_FUNCTION_REJECTED => "CKR_FUNCTION_REJECTED", 153 | CKR_VENDOR_DEFINED => "CKR_VENDOR_DEFINED", 154 | _ => "unknown", 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /xks-axum/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | extern crate core; 5 | 6 | use std::net::IpAddr; 7 | use std::str::FromStr; 8 | use std::{net::SocketAddr, sync::Arc}; 9 | 10 | use axum::extract::Extension; 11 | use axum::middleware; 12 | use axum::routing::get; 13 | use axum::{routing::post, Router}; 14 | use axum_server::tls_rustls::RustlsConfig; 15 | use axum_server::AddrIncomingConfig; 16 | use const_format::concatcp; 17 | use http::{StatusCode, Uri}; 18 | use settings::ServerConfig; 19 | use tower_http::trace::TraceLayer; 20 | use tracing::Level; 21 | use tracing_appender::non_blocking::WorkerGuard; 22 | use tracing_appender::rolling::RollingFileAppender; 23 | use tracing_subscriber::filter::LevelFilter; 24 | use tracing_subscriber::util::SubscriberInitExt; 25 | use tracing_subscriber::{layer::SubscriberExt, Layer}; 26 | 27 | use xks_proxy::{ 28 | handlers::{decrypt, encrypt, get_health_status, get_key_meta_data}, 29 | sigv4::sigv4_auth, 30 | }; 31 | 32 | use crate::settings::{parse_rotation, CIPHERTEXT_METADATA, SETTINGS}; 33 | use crate::xks_proxy::sigv4::XKSS; 34 | use crate::xks_proxy::ErrorName::InvalidUriPathException; 35 | 36 | mod settings; 37 | mod tls; 38 | mod xks_proxy; 39 | 40 | const METADATA: &str = "metadata"; 41 | const ENCRYPT: &str = "encrypt"; 42 | const DECRYPT: &str = "decrypt"; 43 | const HEALTH: &str = "health"; 44 | const KMS_XKS_V1_PATH: &str = "/kms/xks/v1/"; 45 | const URI_PATH_META_DATA: &str = concatcp!(KMS_XKS_V1_PATH, "keys/:key_id/", METADATA); 46 | const URI_PATH_ENCRYPT: &str = concatcp!(KMS_XKS_V1_PATH, "keys/:key_id/", ENCRYPT); 47 | const URI_PATH_DECRYPT: &str = concatcp!(KMS_XKS_V1_PATH, "keys/:key_id/", DECRYPT); 48 | const URI_PATH_HEALTH: &str = concatcp!(KMS_XKS_V1_PATH, HEALTH); 49 | // Used for ALB ping 50 | const URI_PATH_PING: &str = "/ping"; 51 | const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME"); 52 | 53 | const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); 54 | const GIT_HASH: &str = if let Some(hash) = option_env!("GIT_HASH") { 55 | hash 56 | } else { 57 | "unknown" 58 | }; 59 | const VERSION: &str = concatcp!(CARGO_PKG_VERSION, "-", GIT_HASH); 60 | 61 | const PING_RESPONSE: &str = concatcp!("pong from ", CARGO_PKG_NAME, " v", VERSION, "\n"); 62 | 63 | #[tokio::main] 64 | async fn main() { 65 | let _guard = tracing_init(); 66 | let server_config = &SETTINGS.server; 67 | tracing::info!( 68 | service = server_config.service, 69 | region = server_config.region, 70 | "Starting", 71 | ); 72 | 73 | let port_http_ping = server_config.port_http_ping(); 74 | 75 | if server_config.port == port_http_ping { 76 | proxy_server(server_config).await; 77 | } else { 78 | let proxy = tokio::spawn(proxy_server(server_config)); 79 | let http_health_check = tokio::spawn(http_health_check_server(server_config)); 80 | let _ = tokio::join!(proxy, http_health_check); 81 | } 82 | } 83 | 84 | async fn proxy_server(server_config: &ServerConfig) { 85 | // https://docs.rs/axum-extra/0.1.2/axum_extra/middleware/middleware_fn/fn.from_fn.html 86 | let mut router = Router::new(); 87 | for uri_path_prefix in XKSS.keys() { 88 | tracing::trace!(uri_path_prefix, "Adding url paths"); 89 | router = router 90 | .route( 91 | &format!("{uri_path_prefix}{URI_PATH_HEALTH}"), 92 | post(get_health_status::enact), 93 | ) 94 | .route( 95 | &format!("{uri_path_prefix}{URI_PATH_META_DATA}"), 96 | post(get_key_meta_data::enact), 97 | ) 98 | .route( 99 | &format!("{uri_path_prefix}{URI_PATH_ENCRYPT}"), 100 | post(encrypt::enact), 101 | ) 102 | .route( 103 | &format!("{uri_path_prefix}{URI_PATH_DECRYPT}"), 104 | post(decrypt::enact), 105 | ); 106 | } 107 | tracing::trace!("Number of external key stores: {}", XKSS.len()); 108 | router = router 109 | .route(URI_PATH_PING, get(|| async { PING_RESPONSE })) 110 | .fallback(fallback); 111 | let security_config = &SETTINGS.security; 112 | 113 | tracing::info!(is_sigv4_enabled = security_config.is_sigv4_auth_enabled, 114 | is_tls_enabled = security_config.is_tls_enabled, 115 | is_mtls_enabled = security_config.is_mtls_enabled, 116 | secondary_auth = ?security_config.secondary_auth); 117 | 118 | if security_config.is_sigv4_auth_enabled { 119 | router = router.route_layer(middleware::from_fn(sigv4_auth)); 120 | } else { 121 | router = router.route_layer(Extension("".to_string())); 122 | } 123 | 124 | router = router.layer(TraceLayer::new_for_http()); 125 | 126 | if server_config.ciphertext_metadata_b64.is_some() { 127 | // CIPHERTEXT_METADATA.len() eagerly triggers validation of the configuration 128 | tracing::info!( 129 | "Ciphertext Metadata has length of {} bytes.", 130 | CIPHERTEXT_METADATA.len() 131 | ); 132 | } else { 133 | tracing::info!("Ciphertext Metadata is not configured."); 134 | } 135 | 136 | let ip_addr: IpAddr = server_config 137 | .ip 138 | .parse() 139 | .unwrap_or_else(|_| panic!("unable to parse server ip address {}", server_config.ip)); 140 | let socket_addr = SocketAddr::from((ip_addr, server_config.port)); 141 | 142 | let ka = &server_config.tcp_keepalive; 143 | 144 | tracing::info!( 145 | secs = ?ka.tcp_keepalive_secs, 146 | interval_secs = ?ka.tcp_keepalive_interval_secs, 147 | retries = ?ka.tcp_keepalive_retries, 148 | "TCP Keepalive"); 149 | tracing::info!("v{VERSION} listening on {socket_addr} for traffic"); 150 | 151 | if security_config.is_tls_enabled { 152 | let rustls_server_config: rustls::ServerConfig = tls::make_tls_server_config( 153 | SETTINGS.tls.as_ref().expect("missing tls configuration"), 154 | security_config.is_mtls_enabled, 155 | ) 156 | .await 157 | .expect("server tls misconfiguration"); 158 | 159 | let rustls_config: RustlsConfig = RustlsConfig::from_config(Arc::new(rustls_server_config)); 160 | axum_server::bind_rustls(socket_addr, rustls_config) 161 | .addr_incoming_config( 162 | AddrIncomingConfig::default() 163 | .tcp_keepalive(ka.tcp_keepalive_secs) 164 | .tcp_keepalive_interval(ka.tcp_keepalive_interval_secs) 165 | .tcp_keepalive_retries(ka.tcp_keepalive_retries) 166 | .build(), 167 | ) 168 | .serve(router.into_make_service()) 169 | .await 170 | .expect("https server address binding failed"); 171 | } else { 172 | axum_server::bind(socket_addr) 173 | .addr_incoming_config( 174 | AddrIncomingConfig::default() 175 | .tcp_keepalive(ka.tcp_keepalive_secs) 176 | .tcp_keepalive_interval(ka.tcp_keepalive_interval_secs) 177 | .tcp_keepalive_retries(ka.tcp_keepalive_retries) 178 | .build(), 179 | ) 180 | .serve(router.into_make_service()) 181 | .await 182 | .expect("http server address binding failed"); 183 | } 184 | } 185 | 186 | async fn http_health_check_server(server_config: &ServerConfig) { 187 | let health_check_router = Router::new() 188 | .route(URI_PATH_PING, get(|| async { PING_RESPONSE })) 189 | .fallback(fallback); 190 | let ip_addr: IpAddr = server_config 191 | .ip 192 | .parse() 193 | .unwrap_or_else(|_| panic!("unable to parse server ip address {}", server_config.ip)); 194 | let socket_addr = SocketAddr::from((ip_addr, server_config.port_http_ping())); 195 | tracing::info!("http://{socket_addr}/ping available for health check"); 196 | let ka_config = &server_config.tcp_keepalive; 197 | axum_server::bind(socket_addr) 198 | .addr_incoming_config( 199 | AddrIncomingConfig::default() 200 | .tcp_keepalive(ka_config.tcp_keepalive_secs) 201 | .tcp_keepalive_interval(ka_config.tcp_keepalive_interval_secs) 202 | .tcp_keepalive_retries(ka_config.tcp_keepalive_retries) 203 | .build(), 204 | ) 205 | .serve(health_check_router.into_make_service()) 206 | .await 207 | .expect("http health check address binding failed"); 208 | } 209 | 210 | /// Initialize tracing to output to either the stdout or file according to the tracing configurations. 211 | /// Note it's necessary to return the tracing's [WorkerGuard] to [main] for the file logging, if enabled, to work. 212 | /// See [struct.WorkerGuard.html](https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.WorkerGuard.html) 213 | /// for more details. 214 | fn tracing_init() -> Option { 215 | let tracing_config = &SETTINGS.tracing; 216 | let is_stdout_writer_enabled = tracing_config.is_stdout_writer_enabled; 217 | let is_file_writer_enabled = tracing_config.is_file_writer_enabled; 218 | 219 | if !is_stdout_writer_enabled && !is_file_writer_enabled { 220 | eprintln!( 221 | "Tracing to both stdout and file are disabled. Are you sure this is intentional?" 222 | ); 223 | return None; 224 | } 225 | 226 | let level_string = tracing_config 227 | .level 228 | .as_ref() 229 | .expect("Missing tracing level configuration"); 230 | let level = Level::from_str(level_string) 231 | .unwrap_or_else(|_| panic!("unrecognized trace level {}", level_string)); 232 | 233 | // Source: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/index.html#runtime-configuration-with-layers 234 | let mut layers = Vec::new(); 235 | if is_stdout_writer_enabled { 236 | let layer = tracing_subscriber::fmt::layer() 237 | .with_thread_names(true) 238 | .with_line_number(true) 239 | .with_filter(LevelFilter::from_level(level)) 240 | .boxed(); 241 | layers.push(layer); 242 | } 243 | 244 | let guard = if is_file_writer_enabled { 245 | let directory = tracing_config 246 | .directory 247 | .as_ref() 248 | .expect("Missing log directory configuration"); 249 | let file_name_prefix = tracing_config 250 | .file_prefix 251 | .as_ref() 252 | .expect("Missing log file prefix configuration"); 253 | let rotation_kind = tracing_config 254 | .rotation_kind 255 | .as_ref() 256 | .expect("Missing log file rotation kind"); 257 | let rotation = parse_rotation(rotation_kind); 258 | let rolling_file_appender = RollingFileAppender::new(rotation, directory, file_name_prefix); 259 | let (non_blocking, guard) = tracing_appender::non_blocking(rolling_file_appender); 260 | 261 | let layer = tracing_subscriber::fmt::layer() 262 | .with_thread_names(true) 263 | .with_line_number(true) 264 | .with_target(true) 265 | .with_writer(non_blocking) 266 | .with_filter(LevelFilter::from_level(level)) 267 | .boxed(); 268 | 269 | layers.push(layer); 270 | Some(guard) 271 | } else { 272 | None 273 | }; 274 | 275 | tracing_subscriber::registry().with(layers).init(); 276 | tracing::info!(level = level_string, is_file_writer_enabled, "Tracing"); 277 | if is_file_writer_enabled { 278 | tracing::info!( 279 | rotation_kind = tracing_config.rotation_kind.as_ref().unwrap(), 280 | "Tracing file" 281 | ); 282 | } 283 | guard 284 | } 285 | 286 | async fn fallback(uri: Uri) -> (StatusCode, axum::Json) { 287 | InvalidUriPathException.as_axum_error(format!("No route for {uri}")) 288 | } 289 | -------------------------------------------------------------------------------- /xks-axum/src/settings.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::collections::HashSet; 5 | use std::time::Duration; 6 | use std::{env, fs}; 7 | 8 | use base64::engine::general_purpose::STANDARD as Base64; 9 | use base64::Engine; 10 | 11 | use lazy_static::lazy_static; 12 | use serde_derive::Deserialize; 13 | use serde_with::{serde_as, DurationSeconds}; 14 | use tracing::instrument; 15 | use tracing_appender::rolling::Rotation; 16 | 17 | use crate::settings; 18 | 19 | pub const XKS_PROXY_SETTINGS_TOML: &str = "XKS_PROXY_SETTINGS_TOML"; 20 | pub const PKCS11_HSM_MODULE: &str = "PKCS11_HSM_MODULE"; 21 | pub const PKCS11_LOGGER_LIBRARY_PATH: &str = "PKCS11_LOGGER_LIBRARY_PATH"; 22 | pub const PKCS11_LOGGER_LOG_FILE_PATH: &str = "PKCS11_LOGGER_LOG_FILE_PATH"; 23 | pub const PKCS11_LOGGER_FLAGS: &str = "PKCS11_LOGGER_FLAGS"; 24 | 25 | // Used only if the "XKS_PROXY_SETTINGS_TOML" environment variable is not explicitly set 26 | const DEFAULT_BOOTSTRAP_TOML: &str = "configuration/bootstrap.toml"; 27 | 28 | pub const CIPHERTEXT_METATDATA_MAX_BYTE_LENGTH: usize = 20; 29 | 30 | lazy_static! { 31 | pub static ref SETTINGS: Settings = settings::load_settings(); 32 | pub static ref CIPHERTEXT_METADATA: Vec = 33 | load_ciphertext_metadata(&SETTINGS.server.ciphertext_metadata_b64); 34 | } 35 | 36 | #[derive(Debug, Deserialize)] 37 | pub struct Settings { 38 | pub server: ServerConfig, 39 | pub security: SecurityConfig, 40 | pub tls: Option, 41 | pub tracing: TracingConfig, 42 | pub pkcs11: Pkcs11Config, 43 | pub pkcs11_logger: Option, 44 | pub limits: LimitsConfig, 45 | pub hsm_capabilities: HsmCapabilitiesConfig, 46 | pub external_key_stores: Vec, 47 | } 48 | 49 | #[serde_as] 50 | #[derive(Deserialize, Debug)] 51 | pub struct ServerConfig { 52 | pub ip: String, 53 | pub port: u16, 54 | // Port used for http ping. Defaults to 80. 55 | port_http_ping: Option, 56 | pub region: String, 57 | pub service: String, 58 | pub ciphertext_metadata_b64: Option, 59 | pub tcp_keepalive: TcpKeepaliveConfig, 60 | } 61 | 62 | impl ServerConfig { 63 | pub fn port_http_ping(&self) -> u16 { 64 | self.port_http_ping.unwrap_or(80) 65 | } 66 | } 67 | 68 | #[serde_as] 69 | #[derive(Deserialize, Debug, Clone)] 70 | pub struct TcpKeepaliveConfig { 71 | // https://stackoverflow.com/questions/70184303/how-to-serialize-and-deserialize-chronoduration 72 | #[serde_as(as = "Option>")] 73 | pub tcp_keepalive_secs: Option, 74 | 75 | #[serde_as(as = "Option>")] 76 | pub tcp_keepalive_interval_secs: Option, 77 | 78 | pub tcp_keepalive_retries: Option, 79 | } 80 | 81 | #[non_exhaustive] 82 | #[derive(PartialEq, Eq, Debug, Deserialize, Clone)] 83 | pub enum SecondaryAuth { 84 | Oso, 85 | // Can add support for other providers such as OPA 86 | } 87 | 88 | #[derive(Debug, Deserialize, Clone)] 89 | pub struct OsoConfig { 90 | pub polar_file_path: String, 91 | } 92 | 93 | #[derive(Debug, Deserialize, Clone)] 94 | pub struct SecurityConfig { 95 | pub is_sigv4_auth_enabled: bool, 96 | pub is_tls_enabled: bool, 97 | pub is_mtls_enabled: bool, 98 | 99 | pub secondary_auth: Option, 100 | pub oso: Option, 101 | } 102 | 103 | #[derive(Debug, Deserialize, Clone)] 104 | pub struct TLSConfig { 105 | pub tls_cert_pem: String, 106 | pub tls_key_pem: String, 107 | pub mtls_client_ca_pem: Option, 108 | pub mtls_client_dns_name: Option, 109 | } 110 | 111 | pub fn parse_rotation(rotation_kind: &str) -> Rotation { 112 | match rotation_kind.to_uppercase().as_str() { 113 | "MINUTELY" => Rotation::MINUTELY, 114 | "HOURLY" => Rotation::HOURLY, 115 | "DAILY" => Rotation::DAILY, 116 | "NEVER" => Rotation::NEVER, 117 | _ => panic!("Unrecognized rotation kind '{}'", rotation_kind), 118 | } 119 | } 120 | 121 | #[derive(Debug, Deserialize, Clone)] 122 | pub struct TracingConfig { 123 | pub is_stdout_writer_enabled: bool, 124 | pub is_file_writer_enabled: bool, 125 | pub level: Option, 126 | pub directory: Option, 127 | pub file_prefix: Option, 128 | pub rotation_kind: Option, 129 | } 130 | 131 | #[derive(Debug, Deserialize, Clone)] 132 | pub struct ExternalKeyStore { 133 | pub uri_path_prefix: String, 134 | pub sigv4_access_key_id: String, 135 | pub sigv4_secret_access_key: String, 136 | pub xks_key_id_set: HashSet, 137 | } 138 | 139 | #[derive(Debug, Deserialize, Clone)] 140 | pub struct LimitsConfig { 141 | pub max_plaintext_in_base64: usize, 142 | pub max_aad_in_base64: usize, 143 | } 144 | 145 | #[derive(Debug, Deserialize, Clone)] 146 | pub struct HsmCapabilitiesConfig { 147 | pub can_generate_iv: bool, 148 | pub is_zero_iv_required: bool, 149 | } 150 | 151 | #[derive(Debug, Deserialize, Clone)] 152 | #[allow(non_snake_case)] 153 | pub struct Pkcs11Config { 154 | pub session_pool_max_size: usize, 155 | pub session_pool_timeout_milli: u64, 156 | pub user_pin: String, 157 | pub session_eager_close: bool, 158 | 159 | // Overridable by setting the environmental variable "PKCS11_HSM_MODULE" 160 | pub PKCS11_HSM_MODULE: String, 161 | 162 | pub context_read_timeout_milli: u64, 163 | } 164 | 165 | #[derive(Debug, Deserialize, Clone)] 166 | #[allow(non_snake_case)] 167 | pub struct Pkcs11LoggerConfig { 168 | // Overridable by setting the environmental variable "PKCS11_LOGGER_LIBRARY_PATH" 169 | pub PKCS11_LOGGER_LIBRARY_PATH: String, 170 | // Overridable by setting the environmental variable "PKCS11_LOGGER_LOG_FILE_PATH" 171 | pub PKCS11_LOGGER_LOG_FILE_PATH: String, 172 | // Overridable by setting the environmental variable "PKCS11_LOGGER_FLAGS" 173 | pub PKCS11_LOGGER_FLAGS: String, 174 | } 175 | 176 | #[instrument(skip_all)] 177 | pub fn env_value(key: &str, default: &str) -> String { 178 | let val = env::var(key).unwrap_or_else(|_| default.to_string()); 179 | env::set_var(key, &val); 180 | tracing::info!("{key}={val}"); 181 | val 182 | } 183 | 184 | fn load_ciphertext_metadata(ciphertext_metadata_b64: &Option) -> Vec { 185 | match ciphertext_metadata_b64 { 186 | Some(encoded) => match Base64.decode(encoded) { 187 | Ok(decoded) => { 188 | // https://github.com/marshallpierce/rust-base64/issues/189 189 | if &Base64.encode(&decoded) != encoded { 190 | panic!("Misconfiguration: invalid base64 encoding of ciphertext metadata"); 191 | } 192 | if decoded.len() > CIPHERTEXT_METATDATA_MAX_BYTE_LENGTH { 193 | panic!( 194 | "Misconfiguration: ciphertext metadata of length {} must not exceed {}", 195 | decoded.len(), 196 | CIPHERTEXT_METATDATA_MAX_BYTE_LENGTH 197 | ); 198 | } 199 | decoded 200 | } 201 | Err(error) => panic!( 202 | "Failed to base 64 decode the ciphertext metadata configured due to \"{}\"", 203 | error 204 | ), 205 | }, 206 | None => vec![], 207 | } 208 | } 209 | 210 | fn load_settings() -> Settings { 211 | let settings_toml = env::var(XKS_PROXY_SETTINGS_TOML).unwrap_or_else(|_| { 212 | // Use the default specified in the bootstrap.toml 213 | let bootstrap_toml = fs::read_to_string(DEFAULT_BOOTSTRAP_TOML).unwrap_or_else(|_| { 214 | panic!( 215 | "failed to read from the default bootstrap toml file '{}'", 216 | DEFAULT_BOOTSTRAP_TOML 217 | ) 218 | }); 219 | 220 | #[derive(Debug, Deserialize)] 221 | #[allow(non_snake_case)] 222 | pub struct Bootstrap { 223 | // Overridable by setting the environmental variable "XKS_PROXY_SETTINGS_TOML" 224 | XKS_PROXY_SETTINGS_TOML: String, 225 | } 226 | 227 | let boostrap: Bootstrap = toml::from_str(bootstrap_toml.as_str()).unwrap_or_else(|_| { 228 | panic!("failed to load the bootstrap tom file '{}'", bootstrap_toml) 229 | }); 230 | boostrap.XKS_PROXY_SETTINGS_TOML 231 | }); 232 | 233 | let settings = fs::read_to_string(&settings_toml).unwrap_or_else(|_| { 234 | panic!( 235 | "failed to read from the settings toml file '{}'", 236 | settings_toml 237 | ) 238 | }); 239 | toml::from_str(settings.as_str()).unwrap_or_else(|_| { 240 | panic!( 241 | "failed to deserialize the settings toml file '{}'", 242 | settings_toml 243 | ) 244 | }) 245 | } 246 | 247 | #[cfg(test)] 248 | mod settings_test { 249 | use std::collections::HashMap; 250 | use std::env; 251 | use std::net::IpAddr; 252 | use std::str::FromStr; 253 | use std::time::Duration; 254 | 255 | use crate::settings::SecondaryAuth::Oso; 256 | use crate::settings::PKCS11_HSM_MODULE; 257 | use crate::{settings, SETTINGS}; 258 | 259 | #[test] 260 | fn server_settings() { 261 | let server_config = &SETTINGS.server; 262 | assert!(server_config.port > 0); 263 | assert!(!server_config.region.is_empty()); 264 | assert!(!server_config.service.is_empty()); 265 | 266 | let _ip: IpAddr = server_config.ip.parse().unwrap(); 267 | 268 | assert_eq!( 269 | server_config.tcp_keepalive.tcp_keepalive_secs, 270 | Some(Duration::from_secs(60)) 271 | ); 272 | assert_eq!( 273 | server_config.tcp_keepalive.tcp_keepalive_interval_secs, 274 | Some(Duration::from_secs(1)) 275 | ); 276 | assert_eq!(server_config.tcp_keepalive.tcp_keepalive_retries, Some(3)); 277 | } 278 | 279 | #[test] 280 | fn tracing_settings() { 281 | let tracing_config = &SETTINGS.tracing; 282 | if tracing_config.is_file_writer_enabled { 283 | assert!(!tracing_config.directory.as_ref().unwrap().is_empty()); 284 | assert!(!tracing_config.file_prefix.as_ref().unwrap().is_empty()); 285 | assert!( 286 | tracing::log::Level::from_str(tracing_config.level.as_ref().unwrap().as_str()) 287 | .is_ok() 288 | ); 289 | } 290 | } 291 | 292 | #[test] 293 | fn security_and_tls_settings() { 294 | let security_config = &SETTINGS.security; 295 | if let Some(tls) = &SETTINGS.tls { 296 | assert!(!tls.tls_cert_pem.is_empty()); 297 | assert!(!tls.tls_key_pem.is_empty()); 298 | if let Some(mtls_client_ca_pem) = &tls.mtls_client_ca_pem { 299 | assert!(!mtls_client_ca_pem.is_empty()); 300 | } else { 301 | assert!(!security_config.is_mtls_enabled); 302 | } 303 | } else { 304 | assert!(!security_config.is_tls_enabled); 305 | assert!(!security_config.is_mtls_enabled); 306 | } 307 | } 308 | 309 | #[test] 310 | fn oso_settings() { 311 | let security_config = &SETTINGS.security; 312 | let secondary_auth = security_config.secondary_auth.as_ref(); 313 | assert_eq!(*secondary_auth.unwrap(), Oso); 314 | 315 | let oso_config = security_config.oso.as_ref().unwrap(); 316 | assert_eq!(oso_config.polar_file_path, "configuration/oso.polar"); 317 | } 318 | 319 | #[test] 320 | fn external_key_stores() { 321 | let external_key_stores = &SETTINGS.external_key_stores; 322 | assert_ne!(external_key_stores.len(), 0); 323 | println!("\n{external_key_stores:?}"); 324 | 325 | let mut map = HashMap::new(); 326 | for xks in external_key_stores { 327 | if map.insert(xks.uri_path_prefix.as_str(), xks).is_some() { 328 | panic!("uri_path_prefix '{}' must be unique", xks.uri_path_prefix); 329 | } 330 | } 331 | println!("external_key_stores: {map:?}\n"); 332 | } 333 | 334 | #[test] 335 | fn env_var_override() { 336 | let pkcs11_config = &SETTINGS.pkcs11; 337 | // This test failed intermittently for some reasons; so retry 10 times to see how it goes. 338 | for i in 0..10 { 339 | env::set_var(PKCS11_HSM_MODULE, "foo"); 340 | let val = settings::env_value(PKCS11_HSM_MODULE, &pkcs11_config.PKCS11_HSM_MODULE); 341 | if val == "foo" { 342 | println!("env_var_override succeeded upon i: {i}"); 343 | return; 344 | } 345 | } 346 | panic!("failed"); 347 | } 348 | 349 | #[test] 350 | fn env_var_default() { 351 | let pkcs11_config = &SETTINGS.pkcs11; 352 | // This test failed intermittently for some reasons; so retry 10 times to see how it goes. 353 | for i in 0..10 { 354 | env::remove_var(PKCS11_HSM_MODULE); 355 | let val = settings::env_value(PKCS11_HSM_MODULE, &pkcs11_config.PKCS11_HSM_MODULE); 356 | if val == pkcs11_config.PKCS11_HSM_MODULE { 357 | println!("env_var_default succeeded upon i: {i}"); 358 | return; 359 | } 360 | } 361 | panic!("failed"); 362 | } 363 | 364 | #[test] 365 | fn max_plaintext_in_base64() { 366 | let limits_config = &SETTINGS.limits; 367 | assert!(limits_config.max_plaintext_in_base64 > 4096 + 2048); 368 | } 369 | 370 | #[test] 371 | fn max_aad_in_base64() { 372 | let limits_config = &SETTINGS.limits; 373 | assert!(limits_config.max_aad_in_base64 > 8192 + 4096); 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /xks-axum/src/tls.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::convert::TryFrom; 5 | use std::sync::Arc; 6 | use std::time::SystemTime; 7 | use std::{ 8 | fs::File, 9 | io::{BufReader, ErrorKind}, 10 | }; 11 | 12 | use rustls::server::{ClientCertVerified, ClientCertVerifier}; 13 | use rustls::{ 14 | server::AllowAnyAuthenticatedClient, Certificate, DistinguishedNames, PrivateKey, 15 | RootCertStore, ServerConfig, 16 | }; 17 | use rustls_pemfile::Item; 18 | use tokio::io; 19 | use webpki::DnsNameRef; 20 | 21 | use crate::settings::TLSConfig; 22 | 23 | // Originally copied from 24 | // https://github.com/programatik29/axum-server/blob/344a9569e8195673c41e05bc5f46de35b3e273fe/src/tls_rustls/mod.rs#L225-L260 25 | 26 | type BoxError = Box; 27 | pub(crate) fn io_other>(error: E) -> io::Error { 28 | io::Error::new(ErrorKind::Other, error) 29 | } 30 | 31 | // https://github.com/hyperium/tonic/blob/master/examples/src/tls_client_auth/server.rs 32 | // https://discord.com/channels/500028886025895936/942633431626547280/942635956400431115 33 | pub async fn make_tls_server_config( 34 | config: &TLSConfig, 35 | is_mtls_enabled: bool, 36 | ) -> io::Result { 37 | let builder = ServerConfig::builder().with_safe_defaults(); 38 | 39 | let cert = tokio::fs::read(config.tls_cert_pem.as_str()).await?; 40 | let certs = rustls_pemfile::certs(&mut cert.as_ref())?; 41 | let cert_chain = certs.into_iter().map(Certificate).collect(); 42 | 43 | let key = tokio::fs::read(config.tls_key_pem.as_str()).await?; 44 | let key_der = match rustls_pemfile::read_one(&mut key.as_ref())? { 45 | Some(Item::RSAKey(key)) | Some(Item::PKCS8Key(key)) => key, 46 | _ => return Err(io_other("private key not found")), 47 | }; 48 | let private_key = PrivateKey(key_der); 49 | 50 | let server_identity_builder = if is_mtls_enabled { 51 | let root_cert_store = make_client_root_cert_store( 52 | config 53 | .mtls_client_ca_pem 54 | .as_ref() 55 | .expect("missing client CA pem file") 56 | .as_str(), 57 | ); 58 | builder.with_client_cert_verifier(Arc::new(AllowAuthenticatedClient::new( 59 | root_cert_store, 60 | config 61 | .mtls_client_dns_name 62 | .as_ref() 63 | .expect("missing mTLS client dns name configuration"), 64 | ))) 65 | } else { 66 | builder.with_no_client_auth() 67 | }; 68 | server_identity_builder 69 | .with_single_cert(cert_chain, private_key) 70 | .map_err(io_other) 71 | } 72 | 73 | // See example at https://github.com/rustls/rustls/blob/main/rustls-mio/examples/tlsserver.rs 74 | fn make_client_root_cert_store(mtls_client_ca_pem: &str) -> RootCertStore { 75 | let root_certs = load_certs(mtls_client_ca_pem); 76 | let mut client_root_cert_store = RootCertStore::empty(); 77 | for root_cert in root_certs { 78 | client_root_cert_store 79 | .add(&root_cert) 80 | .unwrap_or_else(|_| panic!("failed to add client root CA {:?}", root_cert)); 81 | } 82 | client_root_cert_store 83 | } 84 | 85 | fn load_certs(filename: &str) -> Vec { 86 | let cert_file = File::open(filename) 87 | .unwrap_or_else(|_| panic!("failed to open certificate file '{}'", filename)); 88 | let mut reader = BufReader::new(cert_file); 89 | rustls_pemfile::certs(&mut reader) 90 | .unwrap_or_else(|_| panic!("failed to extract certificates from file '{}'", filename)) 91 | .iter() 92 | .map(|bytes| Certificate(bytes.clone())) 93 | .collect() 94 | } 95 | 96 | // Copied from https://github.com/rustls/rustls/blob/v/0.20.6/rustls/src/verify.rs#L579-L589 97 | fn pki_error(error: webpki::Error) -> rustls::Error { 98 | use webpki::Error::*; 99 | match error { 100 | BadDer | BadDerTime => rustls::Error::InvalidCertificateEncoding, 101 | InvalidSignatureForPublicKey => rustls::Error::InvalidCertificateSignature, 102 | UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => { 103 | rustls::Error::InvalidCertificateSignatureType 104 | } 105 | e => rustls::Error::InvalidCertificateData(format!("invalid peer certificate: {e}")), 106 | } 107 | } 108 | 109 | // Borrowed from https://github.com/rustls/rustls/blob/v/0.20.6/rustls/src/verify.rs#L497-L532 110 | pub struct AllowAuthenticatedClient { 111 | delegate: Arc, 112 | client_dns_name: String, 113 | } 114 | 115 | impl AllowAuthenticatedClient { 116 | /// Construct a new `AllowAuthenticatedClient`. 117 | /// 118 | /// `roots` is the list of trust anchors to use for certificate validation. 119 | pub fn new(roots: RootCertStore, client_dns_name: &str) -> AllowAuthenticatedClient { 120 | Self { 121 | delegate: AllowAnyAuthenticatedClient::new(roots), 122 | client_dns_name: client_dns_name.to_string(), 123 | } 124 | } 125 | } 126 | 127 | impl ClientCertVerifier for AllowAuthenticatedClient { 128 | fn offer_client_auth(&self) -> bool { 129 | true 130 | } 131 | 132 | fn client_auth_root_subjects(&self) -> Option { 133 | self.delegate.client_auth_root_subjects() 134 | } 135 | 136 | fn verify_client_cert( 137 | &self, 138 | end_entity: &Certificate, 139 | intermediates: &[Certificate], 140 | now: SystemTime, 141 | ) -> Result { 142 | // https://github.com/briansmith/webpki/issues/257 143 | let cert = webpki::EndEntityCert::try_from(end_entity.0.as_ref()).map_err(pki_error)?; 144 | let dns_name = DnsNameRef::try_from_ascii_str(self.client_dns_name.as_str()).unwrap(); 145 | cert.verify_is_valid_for_dns_name(dns_name) 146 | .map_err(pki_error)?; 147 | self.delegate 148 | .verify_client_cert(end_entity, intermediates, now) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/handlers/decrypt.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::str; 5 | 6 | use axum::extract::Extension; 7 | use axum::{extract::Path, http::StatusCode, response::IntoResponse}; 8 | use base64::engine::general_purpose::STANDARD as Base64; 9 | use base64::Engine; 10 | use deadpool::unmanaged::Object; 11 | use pkcs11::types::CK_SESSION_HANDLE; 12 | use serde::{Deserialize, Serialize}; 13 | use tracing::{info_span, Instrument}; 14 | 15 | use crate::encrypt::RequestMetadata; 16 | use crate::settings::CIPHERTEXT_METATDATA_MAX_BYTE_LENGTH; 17 | use crate::xks_proxy::handlers::{AadAndDigest, REQUEST_META_DATA}; 18 | use crate::xks_proxy::handlers::{DecryptInput, EncryptionAlgorithm}; 19 | use crate::xks_proxy::ErrorName::ValidationException; 20 | use crate::xks_proxy::{handlers, remove_session_from_pool, XksProxyResult, P11_SESSION_POOL}; 21 | use crate::{DECRYPT, SETTINGS}; 22 | 23 | // Defined per XKS Proxy API spec. 24 | #[derive(Deserialize, Debug)] 25 | #[allow(non_snake_case)] 26 | pub struct DecryptRequest { 27 | requestMetadata: RequestMetadata, 28 | ciphertext: String, 29 | 30 | // Used automatically by the rust-axum framework to reject requests if not correctly specified 31 | #[allow(dead_code)] 32 | encryptionAlgorithm: EncryptionAlgorithm, 33 | 34 | initializationVector: String, 35 | authenticationTag: String, 36 | 37 | ciphertextMetadata: Option, 38 | additionalAuthenticatedData: Option, 39 | } 40 | 41 | // Defined per XKS Proxy API spec. 42 | #[derive(Serialize, Default)] 43 | struct DecryptResponse { 44 | plaintext: String, 45 | } 46 | 47 | pub async fn enact( 48 | Extension(uri_path_prefix): Extension, 49 | Path(key_id): Path, 50 | handlers::Json(payload): handlers::Json, 51 | ) -> XksProxyResult { 52 | // Create a span to include the "kmsRequestId" in tracing 53 | // https://docs.rs/tracing/latest/tracing/span/struct.Span.html#in-asynchronous-code 54 | let span = info_span!( 55 | DECRYPT, 56 | kmsRequestId = payload.requestMetadata.kmsRequestId.as_str() 57 | ); 58 | async move { do_enact(uri_path_prefix, key_id, payload).await } 59 | .instrument(span) 60 | .await 61 | } 62 | 63 | async fn do_enact( 64 | uri_path_prefix: String, 65 | key_id: String, 66 | payload: DecryptRequest, 67 | ) -> XksProxyResult { 68 | tracing::info!( 69 | "{REQUEST_META_DATA}: {}", 70 | serde_json::to_string(&payload.requestMetadata).unwrap_or_else(|_| panic!( 71 | "failed to serialize request metadata {:?}", 72 | &payload.requestMetadata 73 | )) 74 | ); 75 | super::authorize_key_usage(&uri_path_prefix, &key_id).await?; 76 | super::secondary_authorization(&uri_path_prefix, DECRYPT, payload.requestMetadata).await?; 77 | 78 | let session_pool = &P11_SESSION_POOL; 79 | let session_handle_object: Object = 80 | handlers::get_or_create_session(session_pool).await?; 81 | 82 | // Get the secret key from the HSM 83 | let key_handle = match super::get_secret_key_handle(&session_handle_object, key_id.as_str()) { 84 | Ok(object_handle) => object_handle, 85 | Err(failure) => { 86 | return Err(super::before_bubbling_failure( 87 | session_handle_object, 88 | session_pool, 89 | failure, 90 | )) 91 | } 92 | }; 93 | let ciphertext = super::base64_decode(&payload.ciphertext, "ciphertext")?; 94 | let mut iv = super::base64_decode(&payload.initializationVector, "IV")?; 95 | let tag = super::base64_decode(&payload.authenticationTag, "authentication tag")?; 96 | let AadAndDigest { 97 | aad: _, 98 | aad_len, 99 | aad_digest, 100 | } = super::aad_and_digest(&payload.additionalAuthenticatedData)?; 101 | 102 | let ciphertext_metadata = sanitize_ciphertext_metadata(&payload.ciphertextMetadata)?; 103 | let plaintext = match super::do_decrypt( 104 | DecryptInput { 105 | ciphertext, 106 | iv: iv.as_mut_slice(), 107 | tag: tag.as_slice(), 108 | aad_digest, 109 | aad_len, 110 | ciphertext_metadata: ciphertext_metadata.as_slice(), 111 | }, 112 | (*session_handle_object, key_handle), 113 | key_id.as_str(), 114 | ) 115 | .await 116 | { 117 | Ok(plaintext) => plaintext, 118 | Err(failure) => { 119 | return Err(super::before_bubbling_failure( 120 | session_handle_object, 121 | session_pool, 122 | failure, 123 | )) 124 | } 125 | }; 126 | 127 | if SETTINGS.pkcs11.session_eager_close { 128 | tracing::debug!("Eagerly closing pkcs11 session"); 129 | remove_session_from_pool(session_handle_object, session_pool, false); 130 | } 131 | Ok(( 132 | StatusCode::OK, 133 | axum::Json(DecryptResponse { 134 | plaintext: Base64.encode(plaintext), 135 | }), 136 | )) 137 | } 138 | 139 | fn sanitize_ciphertext_metadata( 140 | ciphertext_metadata_b64: &Option, 141 | ) -> XksProxyResult> { 142 | match ciphertext_metadata_b64 { 143 | Some(encoded) => { 144 | let decoded = super::base64_decode(encoded, "ciphertextMetadata")?; 145 | if decoded.len() > CIPHERTEXT_METATDATA_MAX_BYTE_LENGTH { 146 | return Err(ValidationException.as_axum_error(format!( 147 | "ciphertext metadata of length {} must not exceed {CIPHERTEXT_METATDATA_MAX_BYTE_LENGTH}", 148 | decoded.len()))); 149 | } 150 | Ok(decoded) 151 | } 152 | None => Ok(vec![]), 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/handlers/encrypt.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::convert::TryInto; 5 | use std::time::Duration; 6 | use std::{mem, str}; 7 | 8 | use axum::extract::Extension; 9 | use axum::{extract::Path, http::StatusCode, response::IntoResponse}; 10 | use base64::engine::general_purpose::STANDARD as Base64; 11 | use base64::Engine; 12 | use oso::PolarClass; 13 | use pkcs11::types::{ 14 | CKM_AES_GCM, CK_BYTE, CK_GCM_PARAMS, CK_GCM_PARAMS_PTR, CK_MECHANISM, CK_OBJECT_HANDLE, 15 | CK_SESSION_HANDLE, CK_VOID_PTR, 16 | }; 17 | use serde::{Deserialize, Serialize}; 18 | use serde_with::skip_serializing_none; 19 | use tracing::{info_span, instrument, Instrument}; 20 | 21 | use crate::settings::CIPHERTEXT_METADATA; 22 | use crate::xks_proxy::handlers::{ 23 | build_full_aad, context_read_timeout_error, pkcs11_to_http_error, DecryptInput, 24 | EncryptionAlgorithm, 25 | }; 26 | use crate::xks_proxy::handlers::{AadAndDigest, REQUEST_META_DATA}; 27 | use crate::xks_proxy::pkcs11::P11_CONTEXT; 28 | use crate::xks_proxy::ErrorName::UnsupportedOperationException; 29 | use crate::xks_proxy::{handlers, remove_session_from_pool, XksProxyResult, P11_SESSION_POOL}; 30 | use crate::{xks_proxy, ENCRYPT, SETTINGS}; 31 | 32 | use super::{AES_GCM_IV_BYTE_SIZE, AES_GCM_TAG_BIT_SIZE, AES_GCM_TAG_BYTE_SIZE}; 33 | 34 | // Defined per XKS Proxy API spec. 35 | #[skip_serializing_none] 36 | #[derive(Serialize, Deserialize, Debug, Clone, PolarClass)] 37 | #[allow(non_snake_case)] 38 | #[allow(dead_code)] 39 | pub struct RequestMetadata { 40 | #[polar(attribute)] 41 | pub awsPrincipalArn: String, 42 | #[polar(attribute)] 43 | pub kmsOperation: String, 44 | pub kmsRequestId: String, 45 | #[polar(attribute)] 46 | pub kmsKeyArn: String, 47 | #[polar(attribute)] 48 | pub awsSourceVpc: Option, 49 | #[polar(attribute)] 50 | pub awsSourceVpce: Option, 51 | #[polar(attribute)] 52 | pub kmsViaService: Option, 53 | } 54 | 55 | // Defined per XKS Proxy API spec. 56 | // Supported Ciphertext Data Integrity Value Algorithms 57 | #[derive(Debug, Deserialize, Serialize)] 58 | #[allow(non_camel_case_types)] 59 | pub enum CdivAlgorithm { 60 | SHA_256, 61 | } 62 | 63 | // Defined per XKS Proxy API spec. 64 | #[derive(Deserialize, Debug)] 65 | #[allow(non_snake_case)] 66 | pub struct EncryptRequest { 67 | requestMetadata: RequestMetadata, 68 | plaintext: String, 69 | 70 | // Used automatically by the rust-axum framework to reject requests if not correctly specified 71 | #[allow(dead_code)] 72 | encryptionAlgorithm: EncryptionAlgorithm, 73 | 74 | additionalAuthenticatedData: Option, 75 | ciphertextDataIntegrityValueAlgorithm: Option, 76 | } 77 | 78 | // Defined per XKS Proxy API spec. 79 | #[skip_serializing_none] 80 | #[derive(Serialize, Default)] 81 | #[allow(non_snake_case)] 82 | struct EncryptResponse { 83 | ciphertext: String, 84 | initializationVector: String, 85 | authenticationTag: String, 86 | ciphertextMetadata: Option, 87 | ciphertextDataIntegrityValue: Option, 88 | } 89 | 90 | struct PlainBlob<'a> { 91 | plaintext: Vec, 92 | aad_digest: Option>, 93 | aad_len: u16, // 2-bytes per the API spec 94 | ciphertext_metadata: &'a [u8], 95 | } 96 | 97 | struct CipherBlob { 98 | ciphertext: Vec, 99 | iv: Vec, 100 | tag: Vec, 101 | } 102 | 103 | pub async fn enact( 104 | Extension(uri_path_prefix): Extension, 105 | Path(key_id): Path, 106 | handlers::Json(payload): handlers::Json, 107 | ) -> XksProxyResult { 108 | // Create a span to include the "kmsRequestId" in tracing 109 | // https://docs.rs/tracing/latest/tracing/span/struct.Span.html#in-asynchronous-code 110 | let span = info_span!( 111 | ENCRYPT, 112 | kmsRequestId = payload.requestMetadata.kmsRequestId.as_str() 113 | ); 114 | async move { do_enact(uri_path_prefix, key_id, payload).await } 115 | .instrument(span) 116 | .await 117 | } 118 | 119 | async fn do_enact( 120 | uri_path_prefix: String, 121 | key_id: String, 122 | payload: EncryptRequest, 123 | ) -> XksProxyResult { 124 | tracing::info!( 125 | "{REQUEST_META_DATA}: {}", 126 | serde_json::to_string(&payload.requestMetadata).unwrap_or_else(|_| panic!( 127 | "failed to serialize request metadata {:?}", 128 | &payload.requestMetadata 129 | )) 130 | ); 131 | super::authorize_key_usage(&uri_path_prefix, &key_id).await?; 132 | super::secondary_authorization(&uri_path_prefix, ENCRYPT, payload.requestMetadata).await?; 133 | // Check size limits 134 | let limits_config = &SETTINGS.limits; 135 | check_size_limit( 136 | payload.plaintext.len(), 137 | limits_config.max_plaintext_in_base64, 138 | "plaintext", 139 | )?; 140 | if let Some(aad) = &payload.additionalAuthenticatedData { 141 | check_size_limit(aad.len(), limits_config.max_aad_in_base64, "AAD")?; 142 | } 143 | 144 | let session_pool = &P11_SESSION_POOL; 145 | let session_handle_object = handlers::get_or_create_session(session_pool).await?; 146 | 147 | // Get the secret key from the HSM 148 | let key_handle = match super::get_secret_key_handle(&session_handle_object, key_id.as_str()) { 149 | Ok(object_handle) => object_handle, 150 | Err(failure) => { 151 | return Err(super::before_bubbling_failure( 152 | session_handle_object, 153 | session_pool, 154 | failure, 155 | )) 156 | } 157 | }; 158 | 159 | let plaintext = super::base64_decode(&payload.plaintext, "plaintext")?; 160 | let AadAndDigest { 161 | mut aad, 162 | aad_len, 163 | aad_digest, 164 | } = super::aad_and_digest(&payload.additionalAuthenticatedData)?; 165 | 166 | // Encrypt the plaintext 167 | let CipherBlob { 168 | ciphertext, 169 | mut iv, 170 | tag, 171 | } = match do_encrypt( 172 | PlainBlob { 173 | plaintext, 174 | aad_digest: aad_digest.clone(), 175 | aad_len, 176 | ciphertext_metadata: CIPHERTEXT_METADATA.as_slice(), 177 | }, 178 | (*session_handle_object, key_handle), 179 | key_id.as_str(), 180 | ) 181 | .await 182 | { 183 | Ok(result) => result, 184 | Err(failure) => { 185 | return Err(super::before_bubbling_failure( 186 | session_handle_object, 187 | session_pool, 188 | failure, 189 | )) 190 | } 191 | }; 192 | 193 | // [ ||] [ ||] || || 194 | let cdiv = match payload.ciphertextDataIntegrityValueAlgorithm { 195 | None => None, 196 | Some(CdivAlgorithm::SHA_256) => { 197 | let mut data = Vec::new(); 198 | if let Some(bytes) = aad.as_mut() { 199 | data.append(bytes); 200 | } 201 | data.extend_from_slice(CIPHERTEXT_METADATA.as_slice()); 202 | data.append(&mut iv.clone()); 203 | data.append(&mut ciphertext.clone()); 204 | data.append(&mut tag.clone()); 205 | let cdiv = super::sha256_then_b64(data.as_slice()); 206 | tracing::trace!("Decrypt the ciphertext after the cdiv {cdiv} has been generated."); 207 | // Perform a decryption to provide meaningful integrity assurance. 208 | // This must happen after the cdiv is generated, not before; for otherwise it will break the integrity assurance. 209 | if let Err(failure) = super::do_decrypt( 210 | DecryptInput { 211 | ciphertext: ciphertext.clone(), 212 | iv: iv.as_mut_slice(), 213 | tag: tag.as_slice(), 214 | aad_digest, 215 | aad_len, 216 | ciphertext_metadata: CIPHERTEXT_METADATA.as_slice(), 217 | }, 218 | (*session_handle_object, key_handle), 219 | key_id.as_str(), 220 | ) 221 | .await 222 | { 223 | return Err(super::before_bubbling_failure( 224 | session_handle_object, 225 | session_pool, 226 | failure, 227 | )); 228 | } 229 | Some(cdiv) 230 | } 231 | }; 232 | 233 | if SETTINGS.pkcs11.session_eager_close { 234 | tracing::debug!("Eagerly closing pkcs11 session"); 235 | remove_session_from_pool(session_handle_object, session_pool, false); 236 | } 237 | Ok(( 238 | StatusCode::OK, 239 | axum::Json(EncryptResponse { 240 | ciphertext: Base64.encode(ciphertext), 241 | initializationVector: Base64.encode(iv), 242 | authenticationTag: Base64.encode(tag), 243 | ciphertextMetadata: SETTINGS.server.ciphertext_metadata_b64.to_owned(), 244 | ciphertextDataIntegrityValue: cdiv, 245 | }), 246 | )) 247 | } 248 | 249 | #[instrument] 250 | fn check_size_limit(len: usize, max: usize, label: &str) -> XksProxyResult<()> { 251 | tracing::trace!("len: {len} vs max: {max}"); 252 | if len > max { 253 | return Err(UnsupportedOperationException.as_axum_error(format!( 254 | "{label} in base 64 with size of {len} bytes is too large. Maximum: {max} bytes." 255 | ))); 256 | }; 257 | Ok(()) 258 | } 259 | 260 | #[instrument(skip_all)] 261 | async fn do_encrypt( 262 | PlainBlob { 263 | plaintext, 264 | aad_digest, 265 | aad_len, 266 | ciphertext_metadata, 267 | }: PlainBlob<'_>, 268 | (session_handle, key_handle): (CK_SESSION_HANDLE, CK_OBJECT_HANDLE), 269 | key_id: &str, 270 | ) -> XksProxyResult { 271 | let is_aad_specified = aad_digest.is_some(); 272 | let can_generate_iv = SETTINGS.hsm_capabilities.can_generate_iv; 273 | let is_zero_iv_required = SETTINGS.hsm_capabilities.is_zero_iv_required; 274 | let mut iv = if !can_generate_iv { 275 | super::do_generate_random(AES_GCM_IV_BYTE_SIZE, session_handle).await? 276 | } else if is_zero_iv_required { 277 | vec![0; AES_GCM_IV_BYTE_SIZE.try_into().unwrap()] // this is necessary for CloudHSM 278 | } else { 279 | Vec::new() 280 | }; 281 | 282 | let iv_bit_len = (iv.len() << 3).try_into().unwrap(); 283 | tracing::trace!( 284 | "Using IV {} of {} bytes and {iv_bit_len} bits", 285 | hex::encode(&iv), 286 | iv.len(), 287 | ); 288 | 289 | let mut full_aad = build_full_aad(aad_len, aad_digest, ciphertext_metadata); 290 | 291 | let mut gcm_params = CK_GCM_PARAMS { 292 | pIv: iv.as_mut_ptr(), 293 | ulIvLen: iv.len().try_into().unwrap(), 294 | ulIvBits: iv_bit_len, 295 | pAAD: full_aad.as_mut_ptr(), 296 | ulAADLen: full_aad.len().try_into().unwrap(), 297 | ulTagBits: AES_GCM_TAG_BIT_SIZE, 298 | }; 299 | 300 | let mechanism = CK_MECHANISM { 301 | mechanism: CKM_AES_GCM, 302 | pParameter: &mut gcm_params as CK_GCM_PARAMS_PTR as CK_VOID_PTR, 303 | ulParameterLen: mem::size_of_val(&gcm_params).try_into().unwrap(), 304 | }; 305 | 306 | tracing::trace!( 307 | "calling ctx.encrypt_init iv: {}, byte-len: {}, bit-len: {}", 308 | hex::encode(&iv), 309 | iv.len(), 310 | iv.len() << 3 311 | ); 312 | do_encrypt_init(session_handle, key_handle, &mechanism)?; 313 | let plaintext_len = plaintext.len(); 314 | tracing::trace!("ctx.encrypt"); 315 | let encryption_output = { 316 | // This extra scope allows the read lock to get dropped immediately after use 317 | if let Some(ctx_read_guard) = P11_CONTEXT.try_read_for(Duration::from_millis( 318 | SETTINGS.pkcs11.context_read_timeout_milli, 319 | )) { 320 | match ctx_read_guard.encrypt(session_handle, plaintext.as_slice()) { 321 | Ok(bytes) => bytes, 322 | Err(pkcs11_error) => { 323 | return Err(on_pkcs11_encrypt_error( 324 | pkcs11_error, 325 | plaintext_len, 326 | is_aad_specified, 327 | aad_len, 328 | key_id, 329 | )); 330 | } 331 | } 332 | } else { 333 | return Err(context_read_timeout_error()); 334 | } 335 | }; 336 | 337 | let ciphertext_without_tag = encryption_output[..(plaintext_len)].to_vec(); 338 | let tag = 339 | encryption_output[plaintext_len..(plaintext_len + AES_GCM_TAG_BYTE_SIZE as usize)].to_vec(); 340 | 341 | if can_generate_iv && !is_zero_iv_required { 342 | // This is how Luna HSM works. Not sure about other vendors at this stage. 343 | tracing::trace!("Extracting IV from cipher text"); 344 | iv = encryption_output[(plaintext_len + AES_GCM_TAG_BYTE_SIZE as usize)..].to_vec() 345 | } 346 | 347 | Ok(CipherBlob { 348 | ciphertext: ciphertext_without_tag, 349 | iv, 350 | tag, 351 | }) 352 | } 353 | 354 | fn do_encrypt_init( 355 | session_handle: CK_SESSION_HANDLE, 356 | key_handle: CK_OBJECT_HANDLE, 357 | mechanism: &CK_MECHANISM, 358 | ) -> XksProxyResult<()> { 359 | // This function allows the read lock to get dropped immediately after use 360 | if let Some(ctx_read_guard) = P11_CONTEXT.try_read_for(Duration::from_millis( 361 | SETTINGS.pkcs11.context_read_timeout_milli, 362 | )) { 363 | // The following call always failed with CKR_FUNCTION_FAILED against CloudHSM until I did: 364 | // sudo /opt/cloudhsm/bin/configure-pkcs11 --disable-key-availability-check 365 | if let Err(pkcs11_error) = 366 | ctx_read_guard.encrypt_init(session_handle, mechanism, key_handle) 367 | { 368 | let (error_name, pkcs11_errmsg) = pkcs11_to_http_error(&pkcs11_error); 369 | tracing::trace!("calling ctx.get_session_info"); 370 | if let Ok(session_info) = ctx_read_guard.get_session_info(session_handle) { 371 | tracing::warn!( 372 | ?session_info, 373 | pkcs11_errmsg, 374 | ?mechanism, 375 | "Failed to encrypt:" 376 | ); 377 | } 378 | return Err(error_name.as_axum_pkcs11_error( 379 | format!( 380 | "Failed to encrypt with {:?} due to {pkcs11_errmsg}", 381 | &mechanism 382 | ), 383 | pkcs11_error, 384 | )); 385 | } 386 | Ok(()) 387 | } else { 388 | Err(context_read_timeout_error()) 389 | } 390 | } 391 | 392 | fn on_pkcs11_encrypt_error( 393 | pkcs11_error: pkcs11::errors::Error, 394 | plaintext_len: usize, 395 | is_aad_specified: bool, 396 | aad_len: u16, 397 | key_id: &str, 398 | ) -> (StatusCode, axum::Json) { 399 | let aad_msg = if is_aad_specified { 400 | format!("with AAD of {aad_len} bytes") 401 | } else { 402 | "without any AAD".to_string() 403 | }; 404 | 405 | let (error_name, pkcs11_errmsg) = super::pkcs11_to_http_error(&pkcs11_error); 406 | 407 | error_name.as_axum_pkcs11_error( 408 | format!( 409 | "Failed to encrypt plaintext of {plaintext_len} bytes {aad_msg} using key id {key_id} due to {pkcs11_errmsg}", 410 | ), 411 | pkcs11_error, 412 | ) 413 | } 414 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/handlers/get_health_status.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::time::Duration; 5 | 6 | use axum::extract::Extension; 7 | use axum::{http::StatusCode, response::IntoResponse}; 8 | use oso::PolarClass; 9 | use pkcs11::types::{CK_SLOT_ID, CK_TOKEN_INFO}; 10 | use serde::{Deserialize, Serialize}; 11 | use serde_with::skip_serializing_none; 12 | use tracing::{info_span, instrument, Instrument}; 13 | 14 | use crate::xks_proxy::handlers::pkcs11_to_xksproxy_error; 15 | use crate::xks_proxy::handlers::REQUEST_META_DATA; 16 | use crate::xks_proxy::pkcs11::{ 17 | is_ckr_fatal, pkcs11_context_read_timeout_error, pkcs11_error_string, P11_CONTEXT, 18 | }; 19 | use crate::xks_proxy::{handlers, reset_p11_context, XksProxyResult, SETTINGS}; 20 | use crate::HEALTH; 21 | 22 | // Defined per XKS Proxy API spec. 23 | #[skip_serializing_none] 24 | #[derive(Serialize, Deserialize, Debug, Clone, PolarClass)] 25 | #[allow(non_snake_case)] 26 | #[allow(dead_code)] 27 | pub struct RequestMetadata { 28 | kmsRequestId: String, 29 | #[polar(attribute)] 30 | kmsOperation: String, 31 | } 32 | 33 | // Defined per XKS Proxy API spec. 34 | #[derive(Deserialize, Debug)] 35 | #[allow(non_snake_case)] 36 | pub struct GetHealthStatusRequest { 37 | requestMetadata: RequestMetadata, 38 | } 39 | 40 | // Defined per XKS Proxy API spec. 41 | #[derive(Serialize)] 42 | #[allow(non_snake_case)] 43 | struct EkmFleetDetails { 44 | id: String, 45 | model: String, 46 | healthStatus: String, 47 | } 48 | 49 | // Defined per XKS Proxy API spec. 50 | #[derive(Serialize)] 51 | #[allow(non_snake_case)] 52 | struct GetHealthStatusResponse { 53 | xksProxyFleetSize: u16, 54 | xksProxyVendor: String, 55 | xksProxyModel: String, 56 | ekmVendor: String, 57 | ekmFleetDetails: Vec, 58 | } 59 | 60 | #[instrument] 61 | async fn get_token_info() -> XksProxyResult { 62 | let slots_ids = do_get_slot_list().await.map_err(|pkcs11_error| { 63 | tracing::warn!( 64 | "Failed in getting slot list due to {:?}", 65 | pkcs11_error_string(&pkcs11_error) 66 | ); 67 | if is_ckr_fatal(&pkcs11_error) { 68 | reset_p11_context(); 69 | } 70 | pkcs11_to_xksproxy_error(pkcs11_error) 71 | })?; 72 | 73 | let slot_id = slots_ids.first().ok_or_else(|| { 74 | pkcs11_to_xksproxy_error(pkcs11::errors::Error::Module("No slot available")) 75 | })?; 76 | 77 | do_get_token_info(slot_id).await.map_err(|pkcs11_error| { 78 | tracing::warn!( 79 | "Failed in getting token info due to {:?}", 80 | pkcs11_error_string(&pkcs11_error) 81 | ); 82 | if is_ckr_fatal(&pkcs11_error) { 83 | reset_p11_context(); 84 | } 85 | pkcs11_to_xksproxy_error(pkcs11_error) 86 | }) 87 | } 88 | 89 | async fn do_get_slot_list() -> Result, pkcs11::errors::Error> { 90 | // This function allows the read lock to get dropped immediately after use 91 | if let Some(ctx_read_guard) = P11_CONTEXT.try_read_for(Duration::from_millis( 92 | SETTINGS.pkcs11.context_read_timeout_milli, 93 | )) { 94 | Ok(ctx_read_guard.get_slot_list(false)?) 95 | } else { 96 | Err(pkcs11_context_read_timeout_error()) 97 | } 98 | } 99 | 100 | async fn do_get_token_info(slot_id: &CK_SLOT_ID) -> Result { 101 | // This function allows the read lock to get dropped immediately after use 102 | if let Some(ctx_read_guard) = P11_CONTEXT.try_read_for(Duration::from_millis( 103 | SETTINGS.pkcs11.context_read_timeout_milli, 104 | )) { 105 | Ok(ctx_read_guard.get_token_info(*slot_id)?) 106 | } else { 107 | Err(pkcs11_context_read_timeout_error()) 108 | } 109 | } 110 | 111 | pub async fn enact( 112 | Extension(uri_path_prefix): Extension, 113 | handlers::Json(payload): handlers::Json, 114 | ) -> XksProxyResult { 115 | // Create a span to include the "kmsRequestId" in tracing 116 | // https://docs.rs/tracing/latest/tracing/span/struct.Span.html#in-asynchronous-code 117 | let span = info_span!( 118 | HEALTH, 119 | kmsRequestId = payload.requestMetadata.kmsRequestId.as_str() 120 | ); 121 | do_enact(uri_path_prefix, payload).instrument(span).await 122 | } 123 | 124 | async fn do_enact( 125 | uri_path_prefix: String, 126 | payload: GetHealthStatusRequest, 127 | ) -> XksProxyResult { 128 | tracing::info!( 129 | "{REQUEST_META_DATA}: {}", 130 | serde_json::to_string(&payload.requestMetadata).unwrap_or_else(|_| panic!( 131 | "failed to serialize request metadata {:?}", 132 | &payload.requestMetadata 133 | )) 134 | ); 135 | super::secondary_authorization(&uri_path_prefix, HEALTH, payload.requestMetadata).await?; 136 | let token_info = get_token_info().await?; 137 | let firmware_version = token_info.firmwareVersion; 138 | let hardware_version = token_info.hardwareVersion; 139 | 140 | let model = token_info.model; 141 | let label = token_info.label; 142 | let manufacturer_id = token_info.manufacturerID; 143 | let serial_number = token_info.serialNumber; 144 | 145 | // Could be useful in the future to include these info in the response. 146 | // let rw_session_count = token_info.ulRwSessionCount; 147 | // let flags = token_info.flags; 148 | // let session_count = token_info.ulSessionCount; 149 | 150 | tracing::trace!("token_info: {:?}", token_info); 151 | let response = GetHealthStatusResponse { 152 | xksProxyFleetSize: 1, 153 | xksProxyVendor: "AWS-KMS".to_string(), 154 | xksProxyModel: "RustXksProxy".to_string(), 155 | ekmVendor: format!("{manufacturer_id} (serial number: {serial_number})"), 156 | ekmFleetDetails: vec![EkmFleetDetails { 157 | id: label.to_string(), 158 | model: format!( 159 | "{} (firmware version: {}.{}, hardware version: {}.{})", 160 | model, 161 | firmware_version.major, 162 | firmware_version.minor, 163 | hardware_version.major, 164 | hardware_version.minor 165 | ), 166 | healthStatus: "ACTIVE".to_owned(), 167 | }], 168 | }; 169 | 170 | Ok((StatusCode::OK, axum::Json(response))) 171 | } 172 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/handlers/get_key_meta_data.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::ops::Deref; 5 | 6 | use axum::extract::Extension; 7 | use axum::{extract::Path, http::StatusCode, response::IntoResponse}; 8 | use oso::PolarClass; 9 | use pkcs11::types::{ 10 | CKA_CLASS, CKA_DECRYPT, CKA_ENCRYPT, CKA_KEY_TYPE, CKA_SIGN, CKA_UNWRAP, CKA_VALUE_LEN, 11 | CKA_VERIFY, CKA_WRAP, CK_ATTRIBUTE, CK_KEY_TYPE, CK_ULONG, 12 | }; 13 | use serde::{Deserialize, Serialize}; 14 | use serde_with::skip_serializing_none; 15 | use tracing::{info_span, Instrument}; 16 | 17 | use xks_proxy::pkcs11::pkcs11_keytype; 18 | 19 | use crate::xks_proxy::handlers::REQUEST_META_DATA; 20 | use crate::xks_proxy::pkcs11::P11_CONTEXT; 21 | use crate::xks_proxy::{handlers, remove_session_from_pool, XksProxyResult, P11_SESSION_POOL}; 22 | use crate::{xks_proxy, METADATA, SETTINGS}; 23 | 24 | // Defined per XKS Proxy API spec. 25 | #[skip_serializing_none] 26 | #[derive(Serialize, Deserialize, Debug, Clone, PolarClass)] 27 | #[allow(non_snake_case)] 28 | #[allow(dead_code)] 29 | pub struct RequestMetadata { 30 | #[polar(attribute)] 31 | pub awsPrincipalArn: String, 32 | #[polar(attribute)] 33 | pub kmsOperation: String, 34 | pub kmsRequestId: String, 35 | #[polar(attribute)] 36 | pub awsSourceVpc: Option, 37 | #[polar(attribute)] 38 | pub awsSourceVpce: Option, 39 | #[polar(attribute)] 40 | pub kmsKeyArn: Option, 41 | #[polar(attribute)] 42 | pub kmsViaService: Option, 43 | } 44 | 45 | // Defined per XKS Proxy API spec. 46 | #[derive(Deserialize, Debug)] 47 | #[allow(non_snake_case)] 48 | pub struct GetKeyMetadataRequest { 49 | requestMetadata: RequestMetadata, 50 | } 51 | 52 | // Defined per XKS Proxy API spec. 53 | #[derive(Serialize, Debug)] 54 | #[allow(clippy::upper_case_acronyms)] 55 | enum KeyUsage { 56 | ENCRYPT, 57 | DECRYPT, 58 | SIGN, 59 | VERIFY, 60 | WRAP, 61 | UNWRAP, 62 | } 63 | 64 | // Defined per XKS Proxy API spec. 65 | #[derive(Serialize, Default)] 66 | #[allow(non_snake_case)] 67 | struct KeyMetadataResponse { 68 | keySpec: String, 69 | keyUsage: Vec, 70 | keyStatus: String, 71 | } 72 | 73 | pub async fn enact( 74 | Extension(uri_path_prefix): Extension, 75 | Path(key_id): Path, 76 | handlers::Json(payload): handlers::Json, 77 | ) -> XksProxyResult { 78 | // Create a span to include the "kmsRequestId" in tracing 79 | // https://docs.rs/tracing/latest/tracing/span/struct.Span.html#in-asynchronous-code 80 | let span = info_span!( 81 | METADATA, 82 | kmsRequestId = payload.requestMetadata.kmsRequestId.as_str() 83 | ); 84 | async move { do_enact(uri_path_prefix, key_id, payload).await } 85 | .instrument(span) 86 | .await 87 | } 88 | 89 | async fn do_enact( 90 | uri_path_prefix: String, 91 | key_id: String, 92 | payload: GetKeyMetadataRequest, 93 | ) -> XksProxyResult { 94 | tracing::info!( 95 | "{REQUEST_META_DATA}: {}", 96 | serde_json::to_string(&payload.requestMetadata).unwrap_or_else(|_| panic!( 97 | "failed to serialize request metadata {:?}", 98 | &payload.requestMetadata 99 | )) 100 | ); 101 | super::authorize_key_usage(&uri_path_prefix, &key_id).await?; 102 | super::secondary_authorization(&uri_path_prefix, METADATA, payload.requestMetadata).await?; 103 | 104 | let session_pool = &P11_SESSION_POOL; 105 | let session_handle_object = handlers::get_or_create_session(session_pool).await?; 106 | 107 | let mut template = vec![ 108 | CK_ATTRIBUTE::new(CKA_CLASS), 109 | CK_ATTRIBUTE::new(CKA_KEY_TYPE), 110 | CK_ATTRIBUTE::new(CKA_VALUE_LEN), 111 | CK_ATTRIBUTE::new(CKA_ENCRYPT), 112 | CK_ATTRIBUTE::new(CKA_DECRYPT), 113 | CK_ATTRIBUTE::new(CKA_SIGN), 114 | CK_ATTRIBUTE::new(CKA_VERIFY), 115 | CK_ATTRIBUTE::new(CKA_WRAP), 116 | CK_ATTRIBUTE::new(CKA_UNWRAP), 117 | ]; 118 | 119 | let key_class = 0; 120 | let key_type = 0; 121 | let key_size = 0; 122 | 123 | let can_encrypt = 0; 124 | let can_decrypt = 0; 125 | let can_sign = 0; 126 | let can_verify = 0; 127 | let can_wrap = 0; 128 | let can_unwrap = 0; 129 | 130 | let mut i = 0; 131 | template[i].set_ck_ulong(&key_class); 132 | i += 1; 133 | template[i].set_ck_ulong(&key_type); 134 | i += 1; 135 | template[i].set_ck_ulong(&key_size); 136 | i += 1; 137 | 138 | template[i].set_bool(&can_encrypt); 139 | i += 1; 140 | template[i].set_bool(&can_decrypt); 141 | i += 1; 142 | template[i].set_bool(&can_sign); 143 | i += 1; 144 | template[i].set_bool(&can_verify); 145 | i += 1; 146 | template[i].set_bool(&can_wrap); 147 | i += 1; 148 | template[i].set_bool(&can_unwrap); 149 | 150 | let key_handle = 151 | match super::get_secret_key_handle(session_handle_object.deref(), key_id.as_str()) { 152 | Ok(object_handle) => object_handle, 153 | Err(failure) => { 154 | return Err(super::before_bubbling_failure( 155 | session_handle_object, 156 | session_pool, 157 | failure, 158 | )) 159 | } 160 | }; 161 | 162 | if let Err(pkcs11_error) = 163 | P11_CONTEXT 164 | .read() 165 | .get_attribute_value(*session_handle_object, key_handle, &mut template) 166 | { 167 | return Err(super::before_bubbling_failure( 168 | session_handle_object, 169 | session_pool, 170 | xks_proxy::handlers::pkcs11_to_xksproxy_error(pkcs11_error), 171 | )); 172 | } 173 | 174 | let mut usages = Vec::new(); 175 | if can_encrypt == 1 { 176 | usages.push(KeyUsage::ENCRYPT); 177 | } 178 | if can_decrypt == 1 { 179 | usages.push(KeyUsage::DECRYPT); 180 | } 181 | if can_sign == 1 { 182 | usages.push(KeyUsage::SIGN); 183 | } 184 | if can_verify == 1 { 185 | usages.push(KeyUsage::VERIFY); 186 | } 187 | if can_wrap == 1 { 188 | usages.push(KeyUsage::WRAP); 189 | } 190 | if can_unwrap == 1 { 191 | usages.push(KeyUsage::UNWRAP); 192 | } 193 | 194 | if SETTINGS.pkcs11.session_eager_close { 195 | tracing::debug!("Eagerly closing pkcs11 session"); 196 | remove_session_from_pool(session_handle_object, session_pool, false); 197 | } 198 | 199 | Ok(( 200 | StatusCode::OK, 201 | axum::Json(KeyMetadataResponse { 202 | keySpec: keyspec(key_type, key_size), 203 | keyUsage: usages, 204 | keyStatus: "ENABLED".to_owned(), 205 | }), 206 | )) 207 | } 208 | 209 | fn keyspec(key_type: CK_KEY_TYPE, key_byte_size: CK_ULONG) -> String { 210 | let pkcs11_keytype_name = pkcs11_keytype(key_type); 211 | let keytype_name = &pkcs11_keytype_name["CKK_".len()..]; 212 | format!("{keytype_name}_{}", key_byte_size << 3) 213 | } 214 | 215 | #[cfg(test)] 216 | mod pkcs11_test { 217 | use pkcs11::types::{CKK_AES, CKK_RSA}; 218 | 219 | use crate::get_key_meta_data::keyspec; 220 | 221 | #[test] 222 | fn aes_256_keyspec() { 223 | assert_eq!("AES_256", keyspec(CKK_AES, 32)); 224 | } 225 | 226 | #[test] 227 | fn rsa_1024_keyspec() { 228 | assert_eq!("RSA_1024", keyspec(CKK_RSA, 128)); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/handlers/oso_auth.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use oso::PolarClass; 3 | use oso::{Oso, OsoError}; 4 | 5 | use crate::{encrypt, get_health_status, get_key_meta_data, SETTINGS}; 6 | 7 | // Request Metadata for encrypt or decrypt; used in polar files 8 | #[allow(dead_code)] 9 | type EncryptMetadata = encrypt::RequestMetadata; 10 | 11 | // Request Metadata for get_key_meta_data; used in polar files 12 | #[allow(dead_code)] 13 | type GetKeyMetadata = get_key_meta_data::RequestMetadata; 14 | 15 | // Request Metadata for get_health_status; used in polar files 16 | #[allow(dead_code)] 17 | type GetHealthMetadata = get_health_status::RequestMetadata; 18 | 19 | lazy_static! { 20 | pub static ref OSO: Oso = oso().expect("Failed to instantiate OSO"); 21 | } 22 | 23 | fn oso() -> Result { 24 | let mut oso = Oso::new(); 25 | 26 | // Used by secondary auth for Encrypt or Decrypt 27 | oso.register_class( 28 | encrypt::RequestMetadata::get_polar_class_builder() 29 | .name(stringify!(EncryptMetadata)) 30 | .build(), 31 | )?; 32 | // Used by secondary auth for GetKeyMetadata 33 | oso.register_class( 34 | get_key_meta_data::RequestMetadata::get_polar_class_builder() 35 | .name(stringify!(GetKeyMetadata)) 36 | .build(), 37 | )?; 38 | // Used by secondary auth for GetHealthStatus 39 | oso.register_class( 40 | get_health_status::RequestMetadata::get_polar_class_builder() 41 | .name(stringify!(GetHealthMetadata)) 42 | .build(), 43 | )?; 44 | let oso_config = &SETTINGS 45 | .security 46 | .oso 47 | .as_ref() 48 | .expect("Oso misconfiguration"); 49 | let polar_file_path = &oso_config.polar_file_path; 50 | tracing::info!("Loading oso configuration from {polar_file_path}"); 51 | oso.load_files(vec![polar_file_path])?; 52 | Ok(oso) 53 | } 54 | 55 | #[cfg(test)] 56 | mod settings_test { 57 | use crate::xks_proxy::handlers::oso_auth::OSO; 58 | use crate::{encrypt, get_key_meta_data, DECRYPT, ENCRYPT, METADATA}; 59 | 60 | #[test] 61 | fn test_encrypt_secondary_auth() { 62 | let allowed = OSO 63 | .is_allowed( 64 | "access_key_1", 65 | ENCRYPT, 66 | encrypt::RequestMetadata { 67 | awsPrincipalArn: "alice".to_string(), 68 | kmsOperation: "Encrypt".to_string(), 69 | kmsRequestId: "".to_string(), 70 | kmsKeyArn: "key123".to_string(), 71 | awsSourceVpc: None, 72 | awsSourceVpce: None, 73 | kmsViaService: Some("ebs".to_string()), 74 | // kmsViaService: None, 75 | }, 76 | ) 77 | .expect("Should be authorized"); 78 | assert!(allowed); 79 | } 80 | 81 | #[test] 82 | fn test_decrypt_secondary_auth() { 83 | let result = OSO.is_allowed( 84 | "access_key_1", 85 | DECRYPT, 86 | encrypt::RequestMetadata { 87 | awsPrincipalArn: "bob".to_string(), 88 | kmsOperation: "Decrypt".to_string(), 89 | kmsRequestId: "".to_string(), 90 | kmsKeyArn: "key123".to_string(), 91 | awsSourceVpc: None, 92 | awsSourceVpce: None, 93 | kmsViaService: Some("ebs".to_string()), 94 | }, 95 | ); 96 | println!("decrypt {:?}", result); 97 | } 98 | 99 | #[test] 100 | fn test_metadata_secondary_auth() { 101 | let result = OSO.is_allowed( 102 | "access_key_1", 103 | METADATA, 104 | get_key_meta_data::RequestMetadata { 105 | awsPrincipalArn: "bob".to_string(), 106 | kmsOperation: "DescribeKey".to_string(), 107 | kmsKeyArn: None, 108 | awsSourceVpc: None, 109 | awsSourceVpce: None, 110 | kmsViaService: Some("ebs".to_string()), 111 | kmsRequestId: "".to_string(), 112 | }, 113 | ); 114 | println!("metadata {:?}", result); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/handlers/testings/encrypt_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::convert::TryInto; 5 | use std::mem; 6 | use std::path::PathBuf; 7 | 8 | use pkcs11::types::{ 9 | CKF_RW_SESSION, CKF_SERIAL_SESSION, CKM_AES_GCM, CKU_USER, CK_GCM_PARAMS, CK_GCM_PARAMS_PTR, 10 | CK_MECHANISM, CK_VOID_PTR, 11 | }; 12 | use pkcs11::Ctx; 13 | 14 | use crate::xks_proxy; 15 | use crate::xks_proxy::pkcs11::pkcs11_error_string; 16 | 17 | // This was used to test cloudhsm due to CKR_FUNCTION_FAILED. The solution was to run 18 | // sudo /opt/cloudhsm/bin/configure-pkcs11 --disable-key-availability-check 19 | // This code is kept in case similar test is needed in the future for debugging purposes. 20 | // #[test] 21 | #[allow(dead_code)] 22 | fn test_encrypt_init() { 23 | let path_buf = PathBuf::from("/opt/cloudhsm/lib/libcloudhsm_pkcs11.so"); 24 | let ctx = unsafe { Ctx::new_and_initialize(path_buf).unwrap() }; 25 | let slot_ids = ctx.get_slot_list(false).unwrap(); 26 | println!("slot_ids: {:?}", slot_ids); 27 | 28 | let user_pin = ":"; // Replace this as needed 29 | let slot_id = *slot_ids.first().unwrap(); 30 | let session_handle = ctx 31 | .open_session(slot_id, CKF_SERIAL_SESSION | CKF_RW_SESSION, None, None) 32 | .unwrap(); 33 | ctx.login(session_handle, CKU_USER, Some(user_pin)).unwrap(); 34 | let object_handle = xks_proxy::handlers::get_secret_key_handle(&session_handle, "foo").unwrap(); 35 | println!("object_handle: {object_handle}"); 36 | 37 | let mut iv = vec![0; 12]; 38 | let mut aad = "some aad".as_bytes().to_vec(); 39 | let bit_len = 0; 40 | let mut gcm_params = CK_GCM_PARAMS { 41 | pIv: iv.as_mut_ptr(), 42 | ulIvLen: iv.len().try_into().unwrap(), 43 | ulIvBits: bit_len, 44 | pAAD: aad.as_mut_ptr(), 45 | ulAADLen: aad.len().try_into().unwrap(), 46 | ulTagBits: 16 * 8, 47 | }; 48 | println!("gcm_params {:?}", &gcm_params); 49 | 50 | let mechanism = CK_MECHANISM { 51 | mechanism: CKM_AES_GCM, 52 | pParameter: &mut gcm_params as CK_GCM_PARAMS_PTR as CK_VOID_PTR, 53 | ulParameterLen: mem::size_of_val(&gcm_params).try_into().unwrap(), 54 | }; 55 | println!("mechanism {:?}", &mechanism); 56 | 57 | if let Err(pkcs11_err) = ctx.encrypt_init(session_handle, &mechanism, object_handle) { 58 | let err_str = pkcs11_error_string(&pkcs11_err); 59 | println!("calling ctx.get_session_info"); 60 | if let Ok(session_info) = ctx.get_session_info(session_handle) { 61 | println!("ulDeviceError: {}", session_info.ulDeviceError); 62 | println!("session_info: {:?}", session_info); 63 | } 64 | println!("Failed to encrypt with {:?} due to {err_str}", &mechanism); 65 | } else { 66 | println!("encrypt_init succeeded"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/handlers/testings/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | mod encrypt_test; 5 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | extern crate pkcs11 as rust_pkcs11; 5 | use std::time::Duration; 6 | 7 | use deadpool::unmanaged::{Object, Pool, PoolConfig}; 8 | use http::StatusCode; 9 | use lazy_static::lazy_static; 10 | use rust_pkcs11::types::CK_SESSION_HANDLE; 11 | use serde_derive::Serialize; 12 | use tracing::instrument; 13 | 14 | use crate::settings::SETTINGS; 15 | use crate::xks_proxy; 16 | use crate::xks_proxy::pkcs11::{ 17 | is_ckr_device_error, is_ckr_fatal, pkcs11_context_read_timeout_msg, pkcs11_error_string, 18 | P11_CONTEXT, 19 | }; 20 | 21 | pub mod handlers; 22 | pub mod pkcs11; 23 | pub mod sigv4; 24 | 25 | type XksProxyResult = Result)>; 26 | 27 | lazy_static! { 28 | static ref P11_SESSION_POOL: Pool = { 29 | let pkcs11 = &super::settings::SETTINGS.pkcs11; 30 | let config = PoolConfig { 31 | max_size: pkcs11.session_pool_max_size, 32 | timeout: Some(std::time::Duration::from_millis( 33 | pkcs11.session_pool_timeout_milli, 34 | )), 35 | runtime: Some(deadpool::Runtime::Tokio1), 36 | }; 37 | Pool::from_config(&config) 38 | }; 39 | } 40 | 41 | // Defined per XKS Proxy API spec. 42 | #[derive(Clone, Debug, Serialize)] 43 | #[allow(clippy::enum_variant_names)] 44 | pub enum ErrorName { 45 | ValidationException, 46 | 47 | #[allow(dead_code)] 48 | InvalidStateException, 49 | 50 | InvalidCiphertextException, 51 | 52 | #[allow(dead_code)] 53 | InvalidKeyUsageException, 54 | 55 | AuthenticationFailedException, 56 | AccessDeniedException, 57 | KeyNotFoundException, 58 | InvalidUriPathException, 59 | UnsupportedOperationException, 60 | 61 | #[allow(dead_code)] 62 | DependencyTimeoutException, 63 | 64 | InternalException, 65 | } 66 | 67 | impl ErrorName { 68 | fn status_code(&self) -> StatusCode { 69 | match *self { 70 | ErrorName::ValidationException 71 | | ErrorName::InvalidStateException 72 | | ErrorName::InvalidCiphertextException 73 | | ErrorName::InvalidKeyUsageException => StatusCode::BAD_REQUEST, 74 | ErrorName::AuthenticationFailedException => StatusCode::UNAUTHORIZED, 75 | ErrorName::AccessDeniedException => StatusCode::FORBIDDEN, 76 | ErrorName::KeyNotFoundException | ErrorName::InvalidUriPathException => { 77 | StatusCode::NOT_FOUND 78 | } 79 | ErrorName::UnsupportedOperationException => StatusCode::NOT_IMPLEMENTED, 80 | ErrorName::DependencyTimeoutException => StatusCode::SERVICE_UNAVAILABLE, 81 | ErrorName::InternalException => StatusCode::INTERNAL_SERVER_ERROR, 82 | } 83 | } 84 | 85 | fn as_axum_error_impl( 86 | &self, 87 | error_message: String, 88 | pkcs11_error: Option, 89 | ) -> (StatusCode, axum::Json) { 90 | tracing::debug!(error_message); 91 | ( 92 | self.status_code(), 93 | axum::Json(Error { 94 | errorName: self.clone(), 95 | pkcs11_error, 96 | }), 97 | ) 98 | } 99 | 100 | pub fn as_axum_pkcs11_error( 101 | &self, 102 | error_message: String, 103 | pkcs11_error: rust_pkcs11::errors::Error, 104 | ) -> (StatusCode, axum::Json) { 105 | self.as_axum_error_impl(error_message, Some(pkcs11_error)) 106 | } 107 | 108 | pub fn as_axum_error(&self, error_message: String) -> (StatusCode, axum::Json) { 109 | self.as_axum_error_impl(error_message, None) 110 | } 111 | } 112 | 113 | // Defined per XKS Proxy API spec. 114 | #[allow(non_snake_case)] 115 | #[derive(Debug, Serialize)] 116 | pub struct Error { 117 | pub errorName: ErrorName, 118 | 119 | #[serde(skip)] 120 | pub pkcs11_error: Option, 121 | } 122 | 123 | #[instrument(skip_all)] 124 | fn add_session_to_pool(session_handle: CK_SESSION_HANDLE, pool: &Pool) { 125 | if let Err(pool_error) = pool.try_add(session_handle) { 126 | tracing::warn!( 127 | "Failed to add newly created pkcs11 session to the pool: {:?}", 128 | pool_error 129 | ); 130 | } 131 | tracing::info!("pkcs11 session pool status: {:?}", pool.status()); 132 | } 133 | 134 | // !!WARNING!! All read locks acquired on P11_CONTEXT prior to calling this function must be dropped, 135 | // or else it will cause dead lock. 136 | #[instrument(skip_all)] 137 | fn remove_session_from_pool_on_error( 138 | session_handle_object: Object, 139 | pool: &Pool, 140 | pkcs11_err: &rust_pkcs11::errors::Error, 141 | ) { 142 | let is_ckr_fatal = is_ckr_fatal(pkcs11_err); 143 | tracing::warn!( 144 | is_ckr_fatal, 145 | "Removing pkcs11 session from pool due to {pkcs11_err}" 146 | ); 147 | remove_session_from_pool(session_handle_object, pool, is_ckr_device_error(pkcs11_err)) 148 | } 149 | 150 | // !!WARNING!! All read locks acquired on P11_CONTEXT prior to calling this function must be dropped, 151 | // or else it will cause dead lock. 152 | #[instrument(skip_all)] 153 | fn remove_session_from_pool( 154 | session_handle_object: Object, 155 | pool: &Pool, 156 | is_device_error: bool, 157 | ) { 158 | do_close_session(*session_handle_object); 159 | let _ = Object::take(session_handle_object); 160 | let status = pool.status(); 161 | tracing::info!("pool status after session removal: {:?}", status); 162 | if is_device_error && status.size == 0 { 163 | reset_p11_context(); 164 | } 165 | } 166 | 167 | // !!WARNING!! All read locks acquired on P11_CONTEXT prior to calling this function must be dropped, 168 | // or else it will cause dead lock. 169 | #[instrument] 170 | fn reset_p11_context() { 171 | // We don't need to care about timing out on acquiring the write lock, 172 | // since the HSM is in a non-functional state anyway. 173 | tracing::warn!("Resetting pkcs11 context due to device failure. Acquiring a write lock ..."); 174 | let mut ctx_write_guard = P11_CONTEXT.write(); 175 | tracing::info!("Write lock acquired. Clearing the pkcs11 session pool ..."); 176 | 177 | // Clear the session pool. 178 | let pool: &Pool = &P11_SESSION_POOL; 179 | while pool.status().size > 0 { 180 | tracing::info!( 181 | "Removing pkcs11 session entries from pool of size {}", 182 | pool.status().size 183 | ); 184 | if let Err(pool_error) = pool.try_remove() { 185 | tracing::warn!("error in removing pkcs11 session pool entry: {pool_error}",); 186 | } 187 | } 188 | tracing::info!("Cleared pkcs11 session pool. Finalizing existing pkcs11 context ..."); 189 | // Technically there could be race condition, i.e. incoming requests that cause new entries to be added to the pool, 190 | // but we don't need to care. Why? Because operations on those sessions would immediately fail which in turn 191 | // would lead to those entries being immediately removed. 192 | if let Err(pkcs11_error) = ctx_write_guard.finalize() { 193 | tracing::error!( 194 | "Failed to finalize pkcs11 context due to {}", 195 | pkcs11_error_string(&pkcs11_error) 196 | ); 197 | } 198 | tracing::warn!("Creating and initializing a new pkcs11 context"); 199 | *ctx_write_guard = unsafe { pkcs11::new_and_initialize() }; 200 | tracing::info!("Done resetting pkcs11 context"); 201 | } 202 | 203 | fn do_close_session(session_handle: CK_SESSION_HANDLE) { 204 | // This function allows the read lock to get dropped immediately after use 205 | tracing::warn!("Closing pkcs11 session {session_handle}"); 206 | if let Some(ctx_read_guard) = P11_CONTEXT.try_read_for(Duration::from_millis( 207 | SETTINGS.pkcs11.context_read_timeout_milli, 208 | )) { 209 | if let Err(pkcs11_error) = ctx_read_guard.close_session(session_handle) { 210 | tracing::warn!( 211 | "Failed to close pkcs11 session due to {}", 212 | pkcs11::pkcs11_error_string(&pkcs11_error) 213 | ); 214 | } 215 | } else { 216 | tracing::warn!("{} when closing session", pkcs11_context_read_timeout_msg()); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /xks-axum/src/xks_proxy/sigv4.rs: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::settings::ExternalKeyStore; 5 | use axum::middleware::Next; 6 | use axum::{ 7 | body::{Body, Bytes}, 8 | http::Request, 9 | response::{IntoResponse, Response}, 10 | }; 11 | use chrono::Duration; 12 | use lazy_static::lazy_static; 13 | use scratchstack_aws_signature::{ 14 | sigv4_verify, Request as Sigv4Request, SigningKey, SigningKeyKind::KSecret, 15 | }; 16 | use std::collections::HashMap; 17 | 18 | use crate::settings::SETTINGS; 19 | use crate::xks_proxy::ErrorName::{AuthenticationFailedException, InternalException}; 20 | use crate::xks_proxy::XksProxyResult; 21 | use crate::{KMS_XKS_V1_PATH, URI_PATH_PING}; 22 | 23 | lazy_static! { 24 | pub static ref XKSS: HashMap<&'static str, &'static ExternalKeyStore> = { 25 | let map: HashMap<_, _> = SETTINGS 26 | .external_key_stores 27 | .iter() 28 | .map(|external_key_store| { 29 | ( 30 | external_key_store.uri_path_prefix.as_str(), 31 | external_key_store, 32 | ) 33 | }) 34 | .collect(); 35 | assert_eq!( 36 | map.len(), 37 | SETTINGS.external_key_stores.len(), 38 | "Check configuration for duplicate uri path prefixes." 39 | ); 40 | map 41 | }; 42 | } 43 | 44 | // https://github.com/tokio-rs/axum/blob/main/examples/print-request-response/src/main.rs#L40-L55 45 | // https://discord.com/channels/500028886025895936/870760546109116496/941987388979310633 46 | pub async fn sigv4_auth(req: Request, next: Next) -> XksProxyResult { 47 | // Compute the access key id based on the URI path prefix, if any. 48 | let uri_path = &req.uri().path().to_owned(); 49 | if uri_path == URI_PATH_PING { 50 | let res: Response = next.run(req).await; 51 | return Ok(res); 52 | } 53 | let (parts, body) = req.into_parts(); 54 | let body_as_bytes: Option = hyper::body::to_bytes(body).await.ok(); 55 | let body_as_vec_u8: Option> = body_as_bytes.as_ref().map(|bytes| bytes.to_vec()); 56 | let sigv4_req = Sigv4Request::from_http_request_parts(&parts, body_as_vec_u8); 57 | let gsk_req = sigv4_req 58 | .to_get_signing_key_request( 59 | KSecret, 60 | SETTINGS.server.region.as_str(), 61 | SETTINGS.server.service.as_str(), 62 | ) 63 | .map_err(|signature_err| { 64 | AuthenticationFailedException.as_axum_error(signature_err.to_string()) 65 | })?; 66 | 67 | let xks = xks_by_uri_path(uri_path)?; 68 | if xks.sigv4_access_key_id != gsk_req.access_key { 69 | return Err(AuthenticationFailedException.as_axum_error(format!( 70 | "Access key id {} not allowed under the URI path {uri_path}", 71 | gsk_req.access_key 72 | ))); 73 | } 74 | 75 | let signing_key = SigningKey { 76 | kind: KSecret, 77 | key: xks.sigv4_secret_access_key.as_str().as_bytes().to_vec(), 78 | }; 79 | let allowed_mismatch = Some(Duration::minutes(5)); 80 | if let Err(signature_error) = sigv4_verify( 81 | &sigv4_req, 82 | &signing_key, 83 | allowed_mismatch, 84 | SETTINGS.server.region.as_str(), 85 | SETTINGS.server.service.as_str(), 86 | ) { 87 | tracing::warn!("SigV4 failure: {signature_error}"); 88 | return Err(AuthenticationFailedException.as_axum_error(signature_error.to_string())); 89 | } 90 | // Recompose the request as needed by the axum framework to run 91 | let bytes = body_as_bytes.unwrap_or_default(); 92 | let mut req = Request::from_parts(parts, Body::from(bytes)); 93 | req.extensions_mut().insert(xks.uri_path_prefix.clone()); 94 | let res: Response = next.run(req).await; 95 | Ok(res) 96 | } 97 | 98 | fn xks_by_uri_path(uri_path: &str) -> XksProxyResult<&ExternalKeyStore> { 99 | if let Some(pos) = uri_path.rfind(KMS_XKS_V1_PATH) { 100 | let uri_path_prefix = &uri_path[0..pos]; 101 | if let Some(xks) = XKSS.get(uri_path_prefix) { 102 | return Ok(xks); 103 | } 104 | } 105 | 106 | // Defend against a theoretically impossible condition: the request should have 107 | // already been rejected by the axum framework before execution ever gets here. 108 | Err(InternalException.as_axum_error(format!( 109 | "Failed to access keystore by the URI path {uri_path}" 110 | ))) 111 | } 112 | -------------------------------------------------------------------------------- /xks-axum/tls/client_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEJTCCAw2gAwIBAgIKYQ5eOQAAAAAAIDANBgkqhkiG9w0BAQsFADA5MTcwNQYD 3 | VQQDEy5BbWF6b24uY29tIEludGVybmFsIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y 4 | aXR5MB4XDTE4MDcxMTE3MTQyM1oXDTIzMDcxMTE3MjQyM1owKDEmMCQGA1UEAxMd 5 | QW1hem9uLmNvbSBJbmZvU2VjIENBIEc0IEFDTTEwggEiMA0GCSqGSIb3DQEBAQUA 6 | A4IBDwAwggEKAoIBAQDCPkxShLthA5gH9zf6qwO1Fm0HbodMx2kMEgKxkkjYBNTV 7 | u8RgHX+0rUzxuZYPmFEDlEjS6tFQUT4QRMi7sDtEuxUksDBhNpb/s58E+JSWfSOc 8 | t7HErKi6ZFOYB+s4SjPwO8UUnJBLb2CLn85D+CY10pv5zLv0InVPPj6S5ulhVlzC 9 | x2A1UBa0ov+FvhTQ5CDFw0OnNSEAvQnuhez+FFpCjAlUgc3Y7AL70ofk4QdIyiZt 10 | 1qcawU0kzNVaYcU4vfKswNT/9ML9XZI0TS05GGQu9Yz3hWQ9LJSji0PJQ6uKA3/7 11 | UpBTHwL0OtLqc24HYqZlmnT++vDNLpUR64KNWCQtAgMBAAGjggE+MIIBOjASBgNV 12 | HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTP5Qkcguf6elpUDzp1TbNV6ItWJTAf 13 | BgNVHSMEGDAWgBR/jbFOTKKYCtyLJ79iBWk8JRKzwjBlBgNVHR8EXjBcMFqgWKBW 14 | hlRodHRwOi8vcGtpLmFtYXpvbi5jb20vY3JsL0FtYXpvbi5jb20lMjBJbnRlcm5h 15 | bCUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eS5jcmwwcAYIKwYBBQUH 16 | AQEEZDBiMGAGCCsGAQUFBzAChlRodHRwOi8vcGtpLmFtYXpvbi5jb20vY3J0L0Ft 17 | YXpvbi5jb20lMjBJbnRlcm5hbCUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhv 18 | cml0eS5jcnQwCwYDVR0PBAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IBAQAwXXhTkhsA 19 | hso/9LlUh0HJUxdU3mbTxlIDWXnIf1DZ+V/uzeA9EuwE8ChssXRvA3x/Y0XwjrU8 20 | nGjOOlmhAn9dvd3M4kVVBeBz9txpBO6WGyFEN03EMTnc1vplLFUjukwiyPG1xbl1 21 | eltJIpeF93c7/udC7EfXWd23GKKRg5GQPkfwFs+Da77Td2D2IW9YMCQ8cO8Xu+fA 22 | p2VNXNqlmhh9ezoch8etKbe2A2UobOvkE1PU8YIfoqpmtmoZS0RogOG3IIoUGhzC 23 | BhmdVaemwYLW42LvxOpI9P6rnX+IdCSdTmNOZCJo9Ju9g9Vrb/0UR+GS6nGvZK0f 24 | Zd5mv+kVoEIE 25 | -----END CERTIFICATE----- 26 | -----BEGIN CERTIFICATE----- 27 | MIIEDzCCAvegAwIBAgIQV4cU8I3h2atKJXrl167+ITANBgkqhkiG9w0BAQUFADA5 28 | MTcwNQYDVQQDEy5BbWF6b24uY29tIEludGVybmFsIFJvb3QgQ2VydGlmaWNhdGUg 29 | QXV0aG9yaXR5MB4XDTA3MDgzMDE4MDIyNVoXDTI3MDgzMDE4MTA1OVowOTE3MDUG 30 | A1UEAxMuQW1hem9uLmNvbSBJbnRlcm5hbCBSb290IENlcnRpZmljYXRlIEF1dGhv 31 | cml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO6Bh0acNqMWBRt+ 32 | vp6TcA+dKLLqcdRZQRu/Eo8Vzh6JZpZwYGB7glOJqbFWZm7fIWHzqSUUZgHGLHAn 33 | fbOgPno19aW5uG/Z94xAXHEoo6Ird1lIgMtsaoIy8gsNb+FgcsYvr+sUMU9htJuz 34 | uYkvEUGZZ3IIW9+kMUQwNw9U4U3DgQRAvtOCY+fmWha+2CRIC57kQiCkRwrDLzrK 35 | Wm/Or86P8YRaoPyxcBSeFYuBKbqvWOwAqWTW2ZoqxpYGMwLh+JKDxmrUkj8JD4Vy 36 | RnmceSIIHO3NYRinWbu4FAEFxn/6Xap3P3e7/t8PGbIgIgTh6cavnFNZK/0wM3BB 37 | B31guH0CAwEAAaOCAREwggENMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/ 38 | MB0GA1UdDgQWBBR/jbFOTKKYCtyLJ79iBWk8JRKzwjAQBgkrBgEEAYI3FQEEAwIB 39 | ADCBuwYDVR0gBIGzMIGwMIGtBgwrBgEEAaVrgUgBAQEwgZwwcgYIKwYBBQUHAgIw 40 | Zh5kAEEAbQBhAHoAbwBuAC4AYwBvAG0AIABJAG4AdABlAHIAbgBhAGwAIABDAGUA 41 | cgB0AGkAZgBpAGMAYQB0AGUAIABQAHIAYQBjAHQAaQBjAGUAIABTAHQAYQB0AGUA 42 | bQBlAG4AdDAmBggrBgEFBQcCARYaaHR0cDovL3BraS5hbWF6b24uY29tL2Nwcy8w 43 | DQYJKoZIhvcNAQEFBQADggEBAHQBbp49lpDzeZwT0S125zVpKnie8tSgnY0AjW/h 44 | QMHcDSIGCA2j1d8Sx+Kf+0mheRa4fG0Hm5xk0BbdmV61dB9bcMBtZWvmQBlO/iH+ 45 | 7/06oBVkI67FgxRmp/AmI/JubjGP12eWXoX2YXtSvkjsP49f4ya4k2wTNrcypwlr 46 | Fx5+sjnRdOf24IyDGjr/HnoupYPjoDGtgF7hiMXwVD1UFHPhLVxLQojuYDjSLl3I 47 | 5zaeacRPp76IhA8YfNCJO56t4JGEbJsuQqLfIKF7hTDoqpDipJVUBh/WcmOsNiTd 48 | FQccX3klxYIfJOHixp9MdxMRM1bHwX0xZaUX3qBngHz85GU= 49 | -----END CERTIFICATE----- 50 | -------------------------------------------------------------------------------- /xks-axum/tls/server_cert.pem: -------------------------------------------------------------------------------- 1 | *** This file is for testing only. Not for real. *** 2 | -----BEGIN CERTIFICATE----- 3 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 4 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 5 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 6 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 7 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 8 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 9 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 10 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 11 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 12 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 13 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 14 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 15 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 16 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 17 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 18 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 19 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 20 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 21 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 22 | AopvZ09uEQ== 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /xks-axum/tls/server_key.pem: -------------------------------------------------------------------------------- 1 | *** This file is for testing only. Not for real. *** 2 | -----BEGIN PRIVATE KEY----- 3 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 4 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 5 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 6 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 7 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 8 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 9 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 10 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 11 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 12 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 13 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 14 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 15 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 16 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 17 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 18 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 19 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 20 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 21 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 22 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 23 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 24 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 25 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 26 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 27 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 28 | w37pCS7ykL+7gp7V0WShYsw= 29 | -----END PRIVATE KEY----- 30 | --------------------------------------------------------------------------------