├── .clang-format ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── cppcheck.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING.md ├── Config ├── LICENSE ├── NOTICE ├── README.md ├── api ├── CMakeLists.txt ├── src │ └── gmsa_service.cpp └── tests │ ├── CMakeLists.txt │ ├── gmsa_api_integration_test.cpp │ ├── gmsa_test_cli.sh │ ├── gmsa_test_client.cpp │ └── stress_test_scripts │ ├── create_service_accounts.ps1 │ └── credspec_stress_test.txt ├── auth ├── CMakeLists.txt ├── kerberos │ ├── CMakeLists.txt │ └── src │ │ ├── krb.cpp │ │ └── utf16_decode │ │ ├── Program.cs │ │ ├── Program.runtimeconfig.json │ │ ├── build-using-csc.sh │ │ └── utf16_decode.csproj └── kinit_client │ ├── CMakeLists.txt │ ├── README │ ├── autoconf.h │ ├── extern.h │ ├── k5-buf.h │ ├── k5-err.h │ ├── k5-gmt_mktime.h │ ├── k5-int-pkinit.h │ ├── k5-int.h │ ├── k5-platform.h │ ├── k5-plugin.h │ ├── k5-thread.h │ ├── k5-trace.h │ ├── kinit.c │ ├── kinit_kdb.c │ ├── krb5 │ └── authdata_plugin.h │ ├── osconf.h │ ├── port-sockets.h │ └── socket-utils.h ├── cdk ├── Docker │ ├── Dockerfile │ └── build.sh ├── ECS_task_definition │ ├── CredentialsfetcherADStackCredentialsFetcherTaskDefinitiontemplate.json │ └── README.txt └── cdk-domainless-mode │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── app.py │ ├── cdk.json │ ├── cdk │ ├── __init__.py │ └── cdk_stack.py │ ├── cleanup.py │ ├── credspec_template.json │ ├── data.json │ ├── requirements-dev.txt │ ├── requirements.txt │ ├── source.bat │ ├── start_stack.sh │ ├── test-scripts │ ├── README.md │ ├── add_delete_kerberos_leases.py │ ├── create_domain_joined_AD_accounts.ps1 │ ├── create_domain_joined_kerberos_leases.py │ ├── create_non_domain_joined_AD_accounts.ps1 │ ├── create_non_domain_joined_kerberos_leases.py │ └── stress-test │ │ ├── README.md │ │ ├── client.sh │ │ └── server-restart.sh │ └── tests │ ├── copy_credspecs_and_create_task_defs.py │ ├── create_secrets.py │ ├── gmsa.ps1 │ ├── parse_data_from_json.py │ ├── run_e2e_test.py │ ├── run_sql_test.py │ ├── setup_windows_instance.py │ ├── update_inbound_rules.py │ └── update_task_def_and_run_tasks.py ├── code_coverage_using_gcov.sh ├── common ├── CMakeLists.txt ├── constants.h ├── daemon.h └── util.hpp ├── configuration ├── CMakeLists.txt ├── config.h.in └── src │ ├── config.cpp │ └── krb5.conf ├── daemon ├── CMakeLists.txt └── src │ └── daemon.cpp ├── dependencies └── CMakeLists.txt ├── docs └── cf_gmsa_ou.md ├── metadata ├── CMakeLists.txt ├── src │ └── metadata.cpp └── tests │ ├── metadata_invalid_sample.json │ ├── metadata_sample.json │ └── metadata_test.cpp ├── package └── credentials-fetcher.spec ├── protos └── credentialsfetcher.proto ├── renewal ├── CMakeLists.txt ├── src │ └── renewal.cpp └── tests │ └── renewal_test.cpp ├── sample └── java-sample-app │ ├── Dockerfile │ ├── README.md │ └── SQLServerKerberosConnection.java ├── scripts └── systemd │ └── credentials-fetcher.service ├── setup-scripts ├── README.md ├── docker-scripts │ ├── Dockerfile-ubuntu-20.04 │ └── Dockerfile-ubuntu-22.04 └── shell-scripts │ └── ubuntu-22.04-setup.sh ├── suppressions.txt └── test └── tester.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Microsoft 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Right 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Never 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: None 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: MultiLine 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: true 30 | AfterControlStatement: true 31 | AfterEnum: true 32 | AfterFunction: true 33 | AfterNamespace: true 34 | AfterObjCDeclaration: true 35 | AfterStruct: true 36 | AfterUnion: false 37 | AfterExternBlock: true 38 | BeforeCatch: true 39 | BeforeElse: true 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: None 45 | BreakBeforeBraces: Custom 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeComma 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 100 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: true 60 | DeriveLineEnding: true 61 | DerivePointerAlignment: false 62 | DisableFormat: false 63 | ExperimentalAutoDetectBinPacking: false 64 | FixNamespaceComments: true 65 | ForEachMacros: 66 | - foreach 67 | - Q_FOREACH 68 | - BOOST_FOREACH 69 | IncludeBlocks: Preserve 70 | IncludeCategories: 71 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 72 | Priority: 2 73 | SortPriority: 0 74 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 75 | Priority: 3 76 | SortPriority: 0 77 | - Regex: '.*' 78 | Priority: 1 79 | SortPriority: 0 80 | IncludeIsMainRegex: '(Test)?$' 81 | IncludeIsMainSourceRegex: '' 82 | IndentCaseLabels: false 83 | IndentGotoLabels: true 84 | IndentPPDirectives: None 85 | IndentWidth: 4 86 | IndentWrappedFunctionNames: false 87 | JavaScriptQuotes: Leave 88 | JavaScriptWrapImports: true 89 | KeepEmptyLinesAtTheStartOfBlocks: true 90 | MacroBlockBegin: '' 91 | MacroBlockEnd: '' 92 | MaxEmptyLinesToKeep: 1 93 | NamespaceIndentation: All 94 | ObjCBinPackProtocolList: Auto 95 | ObjCBlockIndentWidth: 2 96 | ObjCSpaceAfterProperty: false 97 | ObjCSpaceBeforeProtocolList: true 98 | PenaltyBreakAssignment: 2 99 | PenaltyBreakBeforeFirstCallParameter: 19 100 | PenaltyBreakComment: 300 101 | PenaltyBreakFirstLessLess: 120 102 | PenaltyBreakString: 1000 103 | PenaltyBreakTemplateDeclaration: 10 104 | PenaltyExcessCharacter: 1000000 105 | PenaltyReturnTypeOnItsOwnLine: 1000 106 | PointerAlignment: Left 107 | ReflowComments: true 108 | SortIncludes: true 109 | SortUsingDeclarations: true 110 | SpaceAfterCStyleCast: false 111 | SpaceAfterLogicalNot: false 112 | SpaceAfterTemplateKeyword: true 113 | SpaceBeforeAssignmentOperators: true 114 | SpaceBeforeCpp11BracedList: false 115 | SpaceBeforeCtorInitializerColon: true 116 | SpaceBeforeInheritanceColon: true 117 | SpaceBeforeParens: ControlStatements 118 | SpaceBeforeRangeBasedForLoopColon: true 119 | SpaceInEmptyBlock: false 120 | SpaceInEmptyParentheses: false 121 | SpacesBeforeTrailingComments: 1 122 | SpacesInAngles: false 123 | SpacesInContainerLiterals: true 124 | SpacesInCStyleCastParentheses: false 125 | SpacesInParentheses: true 126 | SpacesInSquareBrackets: false 127 | SpaceBeforeSquareBrackets: false 128 | Standard: Latest 129 | StatementMacros: 130 | - Q_UNUSED 131 | - QT_REQUIRE_VERSION 132 | TabWidth: 4 133 | UseCRLF: false 134 | UseTab: Never 135 | 136 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | *Testing done:* 6 | 7 | ## Merge Checklist 8 | 9 | _Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request._ 10 | 11 | #### General 12 | 13 | - [ ] I have read the [CONTRIBUTING](https://github.com/aws/credentials-fetcher/blob/mainline/CONTRIBUTING.md) doc 14 | - [ ] I used the commit message format described in [CONTRIBUTING](https://github.com/aws/credentials-fetcher/blob/mainline/CONTRIBUTING.md#commit-your-change) 15 | - [ ] I have updated any necessary documentation, including READMEs and comments (where appropriate) 16 | 17 | #### Tests 18 | 19 | - [ ] I have added tests that prove my fix is effective or that my feature works (if appropriate) 20 | - [ ] I have checked that my tests are not configured for a specific environment 21 | 22 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 23 | -------------------------------------------------------------------------------- /.github/workflows/cppcheck.yml: -------------------------------------------------------------------------------- 1 | name: cppcheck-action 2 | on: 3 | push: 4 | branches: 5 | - mainline 6 | pull_request: 7 | branches: [ mainline ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: cppcheck 17 | run: | 18 | sudo apt-get install -y cppcheck 19 | cppcheck --enable=all --force --suppressions-list=suppressions.txt . 20 | echo "cppcheck completed" 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | **/obj/Debug/ 3 | **/bin/Debug/ 4 | **/obj/ 5 | /.vscode/ 6 | .idea 7 | /auth/**/*.exe 8 | credentials-fetcher.sln 9 | cmake-build-debug/ 10 | 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the credentials-fetcher 2 | 3 | Contributions to the Credentials Fetcher should be made via GitHub [pull 4 | requests](https://github.com/aws/credentials-fetcher/pulls) and discussed using 5 | GitHub [issues](https://github.com/aws/credentials-fetcher/issues). 6 | 7 | ### Before you start 8 | 9 | If you would like to make a significant change, it's a good idea to first open 10 | an issue to discuss it. 11 | 12 | ### Submit Pull Requests 13 | 14 | We are always happy to receive code and documentation contributions to the credentials-fetcher Please be aware of the following notes prior to opening a pull request: 15 | 16 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 17 | 18 | 1. You are working against the latest source on the *main* branch. 19 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 20 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 21 | 4. You have tested your change and added tests where appropriate. Wherever possible, pull requests should contain tests as appropriate. Bugfixes should contain tests that exercise the corrected behavior (i.e., the test should fail without the bugfix and pass with it), and new features should be accompanied by tests exercising the feature. 22 | 23 | GitHub provides additional documentation on [Creating a Pull Request](https://help.github.com/articles/creating-a-pull-request/). 24 | 25 | Please remember to: 26 | * Use commit messages (and PR titles) that follow the guidelines under [Commit Your Change](#commit-your-change). 27 | * Send us a pull request, answering any default questions in the pull request interface. 28 | * Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 29 | 30 | ### Commit Your Change 31 | 32 | We use commit messages to update the project version number and generate changelog entries, so it's important for them to follow the right format. Valid commit messages adhere to the [conventional commit][conventional-commit] standard and include a prefix, separated from the rest of the message by a colon and a space. Here are a few examples: 33 | 34 | ``` 35 | feature: add new field for recommendation source 36 | fix: fix the input validation for the gRPC contract 37 | documentation: update contributing documentation 38 | ``` 39 | 40 | Example supported prefixes are listed in the table below. 41 | 42 | | Prefix | Use for... | 43 | |----------------:|:-----------------------------------------------------------------------------------------------| 44 | | `feature` | Adding a new feature. | 45 | | `fix` | Bug fixes. | 46 | | `refactor` | A code refactor. | 47 | | `change` | Any other code change. | 48 | | `documentation` | Documentation changes. | 49 | | `test` | Test changes. | 50 | 51 | Some of the prefixes allow abbreviation ; e.g. `feat` and `docs` are both valid. If you omit a prefix, the commit will be treated as a `change`. 52 | 53 | For the rest of the message, use imperative style and keep things concise but informative. See [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) for guidance. 54 | 55 | ## Licensing 56 | 57 | The Credentials Fetcher is released under an [Apache 58 | 2.0](http://aws.amazon.com/apache-2-0/) license. Any code you submit will be 59 | released under that license. 60 | 61 | For significant changes, we may ask you to sign a [Contributor License 62 | Agreement](http://en.wikipedia.org/wiki/Contributor_License_Agreement). 63 | 64 | ## Amazon Open Source Code of Conduct 65 | 66 | This code of conduct provides guidance on participation in Amazon-managed open source communities, and outlines the process for reporting unacceptable behavior. As an organization and community, we are committed to providing an inclusive environment for everyone. Anyone violating this code of conduct may be removed and banned from the community. 67 | 68 | **Our open source communities endeavor to:** 69 | * Use welcoming and inclusive language; 70 | * Be respectful of differing viewpoints at all times; 71 | * Accept constructive criticism and work together toward decisions; 72 | * Focus on what is best for the community and users. 73 | 74 | **Our Responsibility.** As contributors, members, or bystanders we each individually have the responsibility to behave professionally and respectfully at all times. Disrespectful and unacceptable behaviors include, but are not limited to: 75 | The use of violent threats, abusive, discriminatory, or derogatory language; 76 | * Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, race, political or religious affiliation; 77 | * Posting of sexually explicit or violent content; 78 | * The use of sexualized language and unwelcome sexual attention or advances; 79 | * Public or private [harassment](http://todogroup.org/opencodeofconduct/#definitions) of any kind; 80 | * Publishing private information, such as physical or electronic address, without permission; 81 | * Other conduct which could reasonably be considered inappropriate in a professional setting; 82 | * Advocating for or encouraging any of the above behaviors. 83 | 84 | **Enforcement and Reporting Code of Conduct Issues.** 85 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting opensource-codeofconduct@amazon.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. 86 | 87 | **Attribution.** _This code of conduct is based on the [template](http://todogroup.org/opencodeofconduct) established by the [TODO Group](http://todogroup.org/) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/)._ 88 | -------------------------------------------------------------------------------- /Config: -------------------------------------------------------------------------------- 1 | package.Credentials-Fetcher = { 2 | interfaces = (1.0); 3 | 4 | # Use NoOpBuild. See https://w.amazon.com/index.php/BrazilBuildSystem/NoOpBuild 5 | build-system = no-op; 6 | build-tools = { 7 | 1.0 = { 8 | NoOpBuild = 1.0; 9 | }; 10 | }; 11 | 12 | # Use runtime-dependencies for when you want to bring in additional 13 | # packages when deploying. 14 | # Use dependencies instead if you intend for these dependencies to 15 | # be exported to other packages that build against you. 16 | dependencies = { 17 | 1.0 = { 18 | }; 19 | }; 20 | 21 | runtime-dependencies = { 22 | 1.0 = { 23 | }; 24 | }; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Credentials Fetcher 2 | Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0 4 | 5 | ********************** 6 | THIRD PARTY COMPONENTS 7 | ********************** 8 | This software includes third party software subject to the following copyrights: 9 | 10 | - gRPC - Apache License, Version 2.0 - Copyright 2014 gRPC authors. 11 | - Systemd - Copyright (C) 1991, 1999 Free Software Foundation, Inc. 12 | - krb5 - Copyright (C) 1985-2022 by the Massachusetts Institute of Technology. 13 | - openldap - The OpenLDAP Public License - Copyright 1998-2013 The OpenLDAP Foundation 14 | - boost - Boost Software License - Version 1.0 - August 17th, 2003 15 | - glib2.0 - GNU LIBRARY GENERAL PUBLIC LICENSE - Copyright (C) 1991 Free Software Foundation, Inc. 16 | - openssl - Copyright (c) 1998-2018 The OpenSSL Project 17 | - mono - In general, the runtime and its class libraries are licensed under the 18 | terms of the MIT license, and some third party code is licensed under 19 | the 3-clause BSD license. 20 | 21 | 22 | -------------------------------------------------------------------------------- /api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(api) 4 | 5 | FILE(GLOB SRC_FILES src/*.cpp) 6 | 7 | get_filename_component(credentialsfetcher_proto "../protos/credentialsfetcher.proto" ABSOLUTE) 8 | get_filename_component(credentialsfetcher_proto_path "${credentialsfetcher_proto}" PATH) 9 | message(${credentialsfetcher_proto}) 10 | set(AWSSDK_INSTALL_LIBDIR /usr/lib64) 11 | 12 | set(SERVICE_COMPONENTS s3 secretsmanager sts) 13 | 14 | if(${DISTRO_ID} MATCHES "amzn") 15 | find_package(AWSSDK REQUIRED COMPONENTS ${SERVICE_COMPONENTS}) 16 | endif() 17 | 18 | if(${Protobuf_VERSION} VERSION_LESS "3.21.0.0") 19 | set(credentialsfetcher_proto_sources "${CMAKE_CURRENT_BINARY_DIR}/credentialsfetcher.pb.cc") 20 | set(credentialsfetcher_proto_headers "${CMAKE_CURRENT_BINARY_DIR}/credentialsfetcher.pb.h") 21 | set(credentialsfetcher_grpc_sources "${CMAKE_CURRENT_BINARY_DIR}/credentialsfetcher.grpc.pb.cc") 22 | set(credentialsfetcher_grpc_headers "${CMAKE_CURRENT_BINARY_DIR}/credentialsfetcher.grpc.pb.h") 23 | 24 | add_custom_command( 25 | OUTPUT "${credentialsfetcher_proto_sources}" "${credentialsfetcher_proto_headers}" "${credentialsfetcher_grpc_sources}" "${credentialsfetcher_grpc_headers}" 26 | COMMAND ${_PROTOBUF_PROTOC} 27 | ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}" --cpp_out "${CMAKE_CURRENT_BINARY_DIR}" 28 | -I "${credentialsfetcher_proto_path}" 29 | --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" 30 | "${credentialsfetcher_proto}" 31 | DEPENDS "${credentialsfetcher_proto}") 32 | else() 33 | add_library(proto-objects OBJECT ${credentialsfetcher_proto}) 34 | target_link_libraries(proto-objects PUBLIC protobuf::libprotobuf) 35 | set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") 36 | target_include_directories(proto-objects PUBLIC "$") 37 | cmake_policy(SET CMP0083 NEW) 38 | include(CheckPIESupported) 39 | check_pie_supported() 40 | if(CMAKE_CXX_LINK_PIE_SUPPORTED) 41 | set_property(TARGET proto-objects 42 | PROPERTY POSITION_INDEPENDENT_CODE TRUE) 43 | else() 44 | message(WARNING "PIE is not supported at link time: ${output}.\n" 45 | "PIE link options will not be passed to linker.") 46 | endif() 47 | protobuf_generate( 48 | TARGET proto-objects 49 | OUT_VAR PROTO_GENERATED_FILES 50 | IMPORT_DIRS "${credentialsfetcher_proto_path}" 51 | PROTOC_OUT_DIR "${PROTO_BINARY_DIR}" 52 | DEPENDENCIES "${credentialsfetcher_proto}" 53 | ) 54 | set_source_files_properties(${PROTO_GENERATED_FILES} PROPERTIES SKIP_UNITY_BUILD_INCLUSION on) 55 | protobuf_generate( 56 | TARGET proto-objects 57 | OUT_VAR PROTO_GENERATED_FILES 58 | LANGUAGE grpc 59 | IMPORT_DIRS "${credentialsfetcher_proto_path}" 60 | GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc 61 | PLUGIN "protoc-gen-grpc=\$" 62 | PROTOC_OUT_DIR "${PROTO_BINARY_DIR}" 63 | DEPENDENCIES "${credentialsfetcher_proto}" 64 | ) 65 | set_source_files_properties(${PROTO_GENERATED_FILES} PROPERTIES SKIP_UNITY_BUILD_INCLUSION on) 66 | endif() 67 | 68 | include_directories("${CMAKE_CURRENT_BINARY_DIR}") 69 | 70 | if(${Protobuf_VERSION} VERSION_LESS "3.21.0.0") 71 | list(APPEND SRC_FILES ${credentialsfetcher_proto_sources}) 72 | list(APPEND SRC_FILES ${credentialsfetcher_proto_headers}) 73 | list(APPEND SRC_FILES ${credentialsfetcher_grpc_sources}) 74 | list(APPEND SRC_FILES ${credentialsfetcher_grpc_headers}) 75 | else() 76 | list(APPEND _PROTOBUF_LIBPROTOBUF proto-objects) 77 | endif() 78 | 79 | add_library(cf_gmsa_service_private OBJECT 80 | ${SRC_FILES} 81 | ${credentialsfetcher_proto_sources} 82 | ${credentialsfetcher_proto_headers} 83 | ${credentialsfetcher_grpc_sources} 84 | ${credentialsfetcher_grpc_headers} 85 | ${CMAKE_CURRENT_SOURCE_DIR}/../auth/kerberos/src/krb.cpp 86 | ${CMAKE_CURRENT_SOURCE_DIR}/../auth/kinit_client/kinit.c 87 | ${CMAKE_CURRENT_SOURCE_DIR}/../auth/kinit_client/kinit_kdb.c 88 | ${CMAKE_CURRENT_SOURCE_DIR}/../metadata/src/metadata.cpp 89 | ${CMAKE_CURRENT_SOURCE_DIR}/../metadata/tests/metadata_test.cpp) 90 | 91 | find_path(GLIB_INCLUDE_DIR glib.h "/usr/include" "/usr/include/glib-2.0") 92 | find_path(GLIB_CONFIG_DIR glibconfig.h "/usr/include" "/usr/lib64/glib-2.0/include" "/usr/lib/x86_64-linux-gnu/glib-2.0/include") 93 | 94 | target_include_directories(cf_gmsa_service_private 95 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../common 96 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../auth/kinit_client 97 | ${GLIB_INCLUDE_DIR} 98 | ${GLIB_CONFIG_DIR} 99 | ${CMAKE_BINARY_DIR}) 100 | 101 | cmake_host_system_information(RESULT PRETTY_NAME QUERY DISTRIB_PRETTY_NAME) 102 | cmake_host_system_information(RESULT DISTRO QUERY DISTRIB_INFO) 103 | 104 | if(${DISTRO_ID} MATCHES "ubuntu") 105 | message(STATUS "Linux distro detected as ubuntu") 106 | target_link_libraries(cf_gmsa_service_private 107 | -L/usr/local/lib 108 | grpc++_reflection grpc++ protobuf grpc re2 upb_json_lib upb_textformat_lib upb_collections_lib upb utf8_range_lib z absl_statusor cares gpr absl_status absl_strerror absl_flags absl_flags_internal absl_flags_reflection absl_raw_hash_set absl_hash absl_city absl_bad_variant_access absl_low_level_hash absl_hashtablez_sampler absl_flags_config absl_flags_program_name absl_flags_private_handle_accessor absl_flags_commandlineflag absl_flags_commandlineflag_internal absl_flags_marshalling absl_random_distributions absl_random_seed_sequences absl_random_internal_pool_urbg absl_random_internal_randen absl_random_internal_randen_hwaes absl_random_internal_randen_hwaes_impl absl_random_internal_randen_slow absl_random_internal_platform absl_random_internal_seed_material absl_random_seed_gen_exception absl_cord absl_bad_optional_access absl_cordz_info absl_cord_internal absl_cordz_functions absl_exponential_biased absl_cordz_handle absl_crc_cord_state absl_crc32c absl_crc_internal absl_crc_cpu_detect absl_str_format_internal absl_synchronization absl_stacktrace absl_symbolize absl_debugging_internal absl_demangle_internal absl_graphcycles_internal absl_kernel_timeout_internal absl_malloc_internal absl_time absl_strings absl_int128 absl_string_view absl_throw_delegate absl_strings_internal absl_base absl_spinlock_wait -lrt absl_raw_logging_internal absl_log_severity absl_civil_time absl_time_zone ssl crypto address_sorting 109 | systemd 110 | glib-2.0 111 | jsoncpp 112 | krb5 kadm5srv_mit kdb5 gssapi_krb5 gssrpc 113 | kdb5 gssrpc k5crypto com_err krb5support resolv utf8_validity 114 | absl_log_internal_check_op absl_leak_check absl_die_if_null absl_log_internal_conditions absl_log_internal_message absl_examine_stack absl_log_internal_format absl_log_internal_proto absl_log_internal_nullguard absl_log_internal_log_sink_set absl_log_sink absl_log_entry absl_flags absl_flags_internal absl_flags_marshalling absl_flags_reflection absl_flags_private_handle_accessor absl_flags_commandlineflag absl_flags_commandlineflag_internal absl_flags_config absl_flags_program_name absl_log_initialize absl_log_globals absl_log_internal_globals absl_raw_hash_set absl_hash absl_city absl_low_level_hash absl_hashtablez_sampler absl_statusor absl_status absl_cord absl_cordz_info absl_cord_internal absl_cordz_functions absl_exponential_biased absl_cordz_handle absl_crc_cord_state absl_crc32c absl_crc_internal absl_crc_cpu_detect absl_bad_optional_access absl_str_format_internal absl_strerror absl_synchronization absl_graphcycles_internal absl_kernel_timeout_internal absl_stacktrace absl_symbolize absl_debugging_internal absl_demangle_internal absl_malloc_internal absl_time absl_civil_time absl_time_zone absl_bad_variant_access utf8_validity utf8_range absl_strings absl_string_view absl_strings_internal absl_base rt absl_spinlock_wait absl_int128 absl_throw_delegate absl_raw_logging_internal absl_log_severity) 115 | else() 116 | target_link_libraries(cf_gmsa_service_private 117 | ${_PROTOBUF_LIBPROTOBUF} 118 | ${_REFLECTION} 119 | ${_GRPC_GRPCPP} 120 | systemd 121 | glib-2.0 122 | jsoncpp 123 | krb5 kadm5srv_mit kdb5 gssrpc gssapi_krb5 gssrpc k5crypto 124 | com_err krb5support resolv ${AWSSDK_LINK_LIBRARIES}) 125 | endif() 126 | 127 | enable_testing() 128 | add_subdirectory(tests) 129 | -------------------------------------------------------------------------------- /api/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | # set the project name 4 | project(api-tests) 5 | 6 | enable_testing() 7 | 8 | 9 | file( 10 | COPY ${CMAKE_CURRENT_SOURCE_DIR}/stress_test_scripts/credspec_stress_test.txt 11 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR} 12 | FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 13 | ) 14 | 15 | if (${Protobuf_VERSION} VERSION_GREATER_EQUAL "3.21.0.0") 16 | list(APPEND _PROTOBUF_LIBPROTOBUF proto-objects) 17 | endif() 18 | 19 | add_executable(gmsa_test_client "gmsa_test_client.cpp") 20 | target_link_libraries(gmsa_test_client 21 | cf_gmsa_service_private ${_PROTOBUF_LIBPROTOBUF}) 22 | 23 | if(BUILD_INTEGRATION_TESTS) 24 | add_executable(gmsa_api_integration_test "gmsa_api_integration_test.cpp") 25 | target_link_libraries(gmsa_api_integration_test 26 | cf_gmsa_service_private 27 | ${_PROTOBUF_LIBPROTOBUF} 28 | jsoncpp 29 | gtest 30 | gtest_main) 31 | if (CMAKE_C_LINK_PIE_SUPPORTED) 32 | set_property(TARGET gmsa_api_integration_test 33 | PROPERTY POSITION_INDEPENDENT_CODE TRUE) 34 | endif () 35 | endif () 36 | 37 | cmake_policy(SET CMP0083 NEW) 38 | include(CheckPIESupported) 39 | check_pie_supported() 40 | if (CMAKE_C_LINK_PIE_SUPPORTED) 41 | set_property(TARGET gmsa_test_client 42 | PROPERTY POSITION_INDEPENDENT_CODE TRUE) 43 | endif () 44 | -------------------------------------------------------------------------------- /api/tests/gmsa_test_cli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | for((i=1; i <= 3 ; i++)) 5 | do 6 | service_account_name="WebApp${i}" 7 | domain_name="contoso" 8 | 9 | add_krb_msg="####### Adding kerberos lease #######" 10 | echo ${add_krb_msg} 11 | 12 | add_kerberos_lease=$(grpc_cli call unix:/var/credentials-fetcher/socket/credentials_fetcher.sock AddKerberosLease "credspec_contents: '{\"CmsPlugins\":[\"ActiveDirectory\"],\"DomainJoinConfig\":{\"Sid\":\"S-1-5-21-4217655605-3681839426-3493040985\",\"MachineAccountName\":\"${service_account_name}\",\"Guid\":\"af602f85-d754-4eea-9fa8-fd76810485f1\",\"DnsTreeName\":\"${domain_name}.com\",\"DnsName\":\"${domain_name}.com\",\"NetBiosName\":\"${domain_name}\"},\"ActiveDirectoryConfig\":{\"GroupManagedServiceAccounts\":[{\"Name\":\"${service_account_name}\",\"Scope\":\"${domain_name}.com\"},{\"Name\":\"${service_account_name}\",\"Scope\":\"${domain_name}\"}]}}'") 13 | 14 | echo "Add kerberos lease response: ${add_kerberos_lease}" 15 | add_kerberos_lease_id=$(echo "${add_kerberos_lease}" | grep 'lease_id' | cut -f2 -d: | tr -d ' ' | tr -d '\n') 16 | 17 | echo "Added kerberos ticket for lease_id: ${add_kerberos_lease_id}" 18 | 19 | delete_krb_msg="####### Deleting kerberos lease #######" 20 | echo ${delete_krb_msg} 21 | delete_kerberos_lease=$(grpc_cli call unix:/var/credentials-fetcher/socket/credentials_fetcher.sock DeleteKerberosLease "lease_id: '${add_kerberos_lease_id}'") 22 | 23 | echo "Delete kerberos tickets corresponding to lease: ${delete_kerberos_lease}" 24 | 25 | done 26 | -------------------------------------------------------------------------------- /api/tests/stress_test_scripts/create_service_accounts.ps1: -------------------------------------------------------------------------------- 1 | ##Prerequisites: 2 | # Create managed AD from AWS directory services and create a domain joined window instance 3 | # To install the AD module on Windows Server, run Install-WindowsFeature RSAT-AD-PowerShell 4 | # To install the AD module on Windows 10 version 1809 or later, run Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0' 5 | # To install the AD module on older versions of Windows 10, see https://aka.ms/rsat 6 | # Run the powershell script, replace the values of variables as required 7 | 8 | # set up for the service account prefix and number of accounts 9 | $service_acccount_prefix = "WebApp" 10 | $start_of_service_account = 1 11 | $num_of_service_accounts = 10 # number of service account to be created 12 | [bool]$create_new_service_account = 1 # set it to 1 if need to create new service account, 0 if the existing service account need to be modified 13 | 14 | 15 | # setup configuration for dns and spn prefix 16 | $dns_name = "contoso.com" 17 | $service_principal_prefix = "host/" 18 | 19 | 20 | # hosts privileged to retrieve servive account password, name of the host machine 21 | $access_privilege_hosts = "ec2amaz-d0t0bn$", "ec2amaz-rgwzzl$" 22 | 23 | # Example : New-ADServiceAccount -Name "WebApp04" -DnsHostName "WebApp04.contoso.com" -ServicePrincipalNames "host/WebApp04", "host/WebApp04.contoso.com" -PrincipalsAllowedToRetrieveManagedPassword "admin","ec2amaz-t8qznk$" 24 | # Set-ADServiceAccount -Identity "WebApp03" -PrincipalsAllowedToRetrieveManagedPassword "WebApp01Hosts","admin","ec2amaz-t8qznk$","ec2amaz-gqebkn$","ec2amaz-uzcxba$", "ec2amaz-d0t0bn$", "ec2amaz-rgwzzl$" 25 | 26 | for ($i=$start_of_service_account; $i -lt $start_of_service_account+$num_of_service_accounts; $i++) 27 | { 28 | 29 | $service_account_name = -join("$service_acccount_prefix", "$i") 30 | $dns_host_name = -join("$service_account_name", ".", "$dns_name") 31 | 32 | 33 | $service_principal_names = -join("$service_principal_prefix", "$service_account_name"), -join("$service_principal_prefix", "$dns_host_name"); 34 | try 35 | { 36 | if($create_new_service_account) 37 | { 38 | #Build cmd to create and run service account 39 | New-ADServiceAccount -Name $service_account_name -DnsHostName $dns_host_name -ServicePrincipalNames $service_principal_names -PrincipalsAllowedToRetrieveManagedPassword $access_privilege_hosts 40 | } 41 | else 42 | { 43 | 44 | Set-ADServiceAccount -Identity $service_account_name -PrincipalsAllowedToRetrieveManagedPassword $access_privilege_hosts 45 | } 46 | 47 | } 48 | Catch 49 | { 50 | # Catch any error 51 | Write-Host "issue with creating/updating service account or service account already created $service_account_name" 52 | } 53 | 54 | } 55 | 56 | #time for the host machine process the accounts created 57 | Start-Sleep -Milliseconds 2000 58 | 59 | 60 | 61 | # generate credspecs for service accounts 62 | 63 | for ($i=$start_of_service_account; $i -lt $start_of_service_account+$num_of_service_accounts; $i++) 64 | { 65 | $service_account_name = -join("$service_acccount_prefix", "$i") 66 | try 67 | { 68 | New-CredentialSpec -AccountName $service_account_name 69 | } 70 | catch 71 | { 72 | # Catch any error 73 | Write-Host "issue generating credspec/ credspec already generated for the $service_account_name" 74 | } 75 | } -------------------------------------------------------------------------------- /api/tests/stress_test_scripts/credspec_stress_test.txt: -------------------------------------------------------------------------------- 1 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp1","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp1","Scope":"contoso.com"},{"Name":"WebApp1","Scope":"contoso"}]}} 2 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp2","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp2","Scope":"contoso.com"},{"Name":"WebApp2","Scope":"contoso"}]}} 3 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp3","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp3","Scope":"contoso.com"},{"Name":"WebApp3","Scope":"contoso"}]}} 4 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp4","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp4","Scope":"contoso.com"},{"Name":"WebApp4","Scope":"contoso"}]}} 5 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp5","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp5","Scope":"contoso.com"},{"Name":"WebApp5","Scope":"contoso"}]}} 6 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp6","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp6","Scope":"contoso.com"},{"Name":"WebApp6","Scope":"contoso"}]}} 7 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp7","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp7","Scope":"contoso.com"},{"Name":"WebApp7","Scope":"contoso"}]}} 8 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp8","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp8","Scope":"contoso.com"},{"Name":"WebApp8","Scope":"contoso"}]}} 9 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp9","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp9","Scope":"contoso.com"},{"Name":"WebApp9","Scope":"contoso"}]}} 10 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp10","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp10","Scope":"contoso.com"},{"Name":"WebApp10","Scope":"contoso"}]}} 11 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp11","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp11","Scope":"contoso.com"},{"Name":"WebApp11","Scope":"contoso"}]}} 12 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp12","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp12","Scope":"contoso.com"},{"Name":"WebApp12","Scope":"contoso"}]}} 13 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp13","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp13","Scope":"contoso.com"},{"Name":"WebApp13","Scope":"contoso"}]}} 14 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp14","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp14","Scope":"contoso.com"},{"Name":"WebApp14","Scope":"contoso"}]}} 15 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp15","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp15","Scope":"contoso.com"},{"Name":"WebApp15","Scope":"contoso"}]}} 16 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp16","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp16","Scope":"contoso.com"},{"Name":"WebApp16","Scope":"contoso"}]}} 17 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp17","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp17","Scope":"contoso.com"},{"Name":"WebApp17","Scope":"contoso"}]}} 18 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp18","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp18","Scope":"contoso.com"},{"Name":"WebApp18","Scope":"contoso"}]}} 19 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp19","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp19","Scope":"contoso.com"},{"Name":"WebApp19","Scope":"contoso"}]}} 20 | {"CmsPlugins":["ActiveDirectory"],"DomainJoinConfig":{"Sid":"S-1-5-21-4217655605-3681839426-3493040985","MachineAccountName":"WebApp20","Guid":"af602f85-d754-4eea-9fa8-fd76810485f1","DnsTreeName":"contoso.com","DnsName":"contoso.com","NetBiosName":"contoso"},"ActiveDirectoryConfig":{"GroupManagedServiceAccounts":[{"Name":"WebApp20","Scope":"contoso.com"},{"Name":"WebApp20","Scope":"contoso"}]}} -------------------------------------------------------------------------------- /auth/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | add_subdirectory (kerberos) 4 | 5 | FILE(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS kerberos/src/*.cpp kerberos/src/*.c kinit_client/*.c) 6 | 7 | set(auth "${SRC_FILES}" PARENT_SCOPE) 8 | 9 | -------------------------------------------------------------------------------- /auth/kerberos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(kerberos) 4 | 5 | enable_testing () 6 | -------------------------------------------------------------------------------- /auth/kerberos/src/utf16_decode/Program.cs: -------------------------------------------------------------------------------- 1 | // Windows seems to use UTF-16. 2 | // For Linux use, UTF-16 strings have to be 3 | // translated to UTF-8. 4 | // 5 | class EncodeToUTF8 { 6 | public static void Main() { 7 | const int inputBufferSize = 1024; 8 | // Reading beyond 1K is not needed for gMSA purposes 9 | 10 | byte[] utf16Bytes = new byte[inputBufferSize]; 11 | 12 | System.IO.Stream inputStream = System.Console.OpenStandardInput(); 13 | inputStream.Read(utf16Bytes, 0, inputBufferSize); 14 | 15 | System.Text.Encoding utf8 = System.Text.Encoding.UTF8; 16 | System.Text.Encoding utf16 = System.Text.Encoding.Unicode; 17 | 18 | byte[] utf8Bytes = System.Text.Encoding.Convert(System.Text.Encoding.Unicode, 19 | System.Text.Encoding.UTF8, utf16Bytes); 20 | System.IO.Stream outputStream = System.Console.OpenStandardOutput(); 21 | outputStream.Write(utf8Bytes); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /auth/kerberos/src/utf16_decode/Program.runtimeconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeOptions": { 3 | "framework": { 4 | "name": "Microsoft.NETCore.App", 5 | "version": "8.0.0" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /auth/kerberos/src/utf16_decode/build-using-csc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Thanks to https://github.com/dotnet/sdk/issues/8742#issuecomment-890559867 4 | 5 | DOTNET_CLI_TELEMETRY_OPTOUT=1 6 | export DOTNET_CLI_TELEMETRY_OPTOUT 7 | 8 | sdkver=$(LC_ALL=C dotnet --version) 9 | fwkver=$(LC_ALL=C dotnet --list-runtimes | \ 10 | LC_ALL=C sed --posix -n '/^Microsoft.NETCore.App \([^ ]*\) .*$/{s//\1/p;q;}') 11 | 12 | dotnethome=/usr/lib/dotnet 13 | if [ -d /usr/lib64/dotnet ]; then 14 | dotnethome=/usr/lib64/dotnet 15 | fi 16 | echo "dotnethome=$dotnethome" 17 | 18 | dotnetlib=$dotnethome/shared/Microsoft.NETCore.App/$fwkver 19 | if [ -d /usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/$fwkver/ref/net8.0/ ]; then 20 | dotnetlib=/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/$fwkver/ref/net8.0/ 21 | elif [ -d /usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/$fwkver/ref/net6.0/ ]; then 22 | dotnetlib=/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/$fwkver/ref/net6.0/ 23 | fi 24 | echo "dotnetlib=$dotnetlib" 25 | 26 | dotnet_cscdll=$dotnethome/sdk/$sdkver/Roslyn/bincore/csc.dll 27 | if [ -f /usr/share/dotnet/sdk/$sdkver/Roslyn/bincore/csc.dll ]; then 28 | dotnet_cscdll=/usr/share/dotnet/sdk/$sdkver/Roslyn/bincore/csc.dll 29 | fi 30 | echo "dotnet_cscdll=$dotnet_cscdll" 31 | 32 | dotnet_csclib='-r:netstandard.dll -r:Microsoft.CSharp.dll -r:System.dll' 33 | for x in "$dotnetlib"/System.*.dll; do 34 | dotnet_csclib="$dotnet_csclib -r:${x##*/}" 35 | done 36 | echo "dotnet_csclib=$dotnet_csclib" 37 | # add if needed 38 | #dotnet_csclib="$dotnet_csclib -r:Microsoft.Win32.Primitives.dll" 39 | 40 | exec dotnet "$dotnet_cscdll" "-lib:$dotnetlib" $dotnet_csclib "$@" 41 | 42 | #!/bin/sh 43 | 44 | DOTNET_CLI_TELEMETRY_OPTOUT=1 45 | export DOTNET_CLI_TELEMETRY_OPTOUT 46 | 47 | sdkver=$(LC_ALL=C dotnet --version) 48 | fwkver=$(LC_ALL=C dotnet --list-runtimes | \ 49 | LC_ALL=C sed --posix -n '/^Microsoft.NETCore.App \([^ ]*\) .*$/{s//\1/p;q;}') 50 | 51 | exename=$1 52 | case $exename in 53 | (*.exe|*.EXE) ;; 54 | (*) 55 | echo >&2 "E: $exename is not a .exe file" 56 | exit 1 57 | ;; 58 | esac 59 | 60 | jsonname=${exename%.*}.runtimeconfig.json 61 | printf '%s"%s"%s\n' \ 62 | '{"runtimeOptions":{"framework":{"name":"Microsoft.NETCore.App","version":' \ 63 | "$fwkver" '}}}' >"$jsonname" -------------------------------------------------------------------------------- /auth/kerberos/src/utf16_decode/utf16_decode.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | linux-x64 10 | true 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /auth/kinit_client/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(kinit_client) 4 | 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") 6 | 7 | FILE(GLOB SRC_FILES *.c) 8 | 9 | set(kinit_client "${SRC_FILES}" PARENT_SCOPE) 10 | -------------------------------------------------------------------------------- /auth/kinit_client/extern.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* clients/kinit/extern.h - Global declarations for kinit */ 3 | /* 4 | * Copyright (C) 2010 by the Massachusetts Institute of Technology. 5 | * All rights reserved. 6 | * 7 | * Export of this software from the United States of America may 8 | * require a specific license from the United States Government. 9 | * It is the responsibility of any person or organization contemplating 10 | * export to obtain such a license before exporting. 11 | * 12 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 | * distribute this software and its documentation for any purpose and 14 | * without fee is hereby granted, provided that the above copyright 15 | * notice appear in all copies and that both that copyright notice and 16 | * this permission notice appear in supporting documentation, and that 17 | * the name of M.I.T. not be used in advertising or publicity pertaining 18 | * to distribution of the software without specific, written prior 19 | * permission. Furthermore if you modify this software you must label 20 | * your software as modified software and not distribute it in such a 21 | * fashion that it might be confused with the original M.I.T. software. 22 | * M.I.T. makes no representations about the suitability of 23 | * this software for any purpose. It is provided "as is" without express 24 | * or implied warranty. 25 | */ 26 | 27 | #ifndef KINIT_EXTERN_H 28 | #define KINIT_EXTERN_H 29 | 30 | krb5_error_code kinit_kdb_init(krb5_context *pcontext, char *realm); 31 | void kinit_kdb_fini(void); 32 | 33 | #endif /* KINIT_EXTERN_H */ 34 | -------------------------------------------------------------------------------- /auth/kinit_client/k5-buf.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* include/k5-buf.h - k5buf interface declarations */ 3 | /* 4 | * Copyright 2008 Massachusetts Institute of Technology. 5 | * All Rights Reserved. 6 | * 7 | * Export of this software from the United States of America may 8 | * require a specific license from the United States Government. 9 | * It is the responsibility of any person or organization contemplating 10 | * export to obtain such a license before exporting. 11 | * 12 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 | * distribute this software and its documentation for any purpose and 14 | * without fee is hereby granted, provided that the above copyright 15 | * notice appear in all copies and that both that copyright notice and 16 | * this permission notice appear in supporting documentation, and that 17 | * the name of M.I.T. not be used in advertising or publicity pertaining 18 | * to distribution of the software without specific, written prior 19 | * permission. Furthermore if you modify this software you must label 20 | * your software as modified software and not distribute it in such a 21 | * fashion that it might be confused with the original M.I.T. software. 22 | * M.I.T. makes no representations about the suitability of 23 | * this software for any purpose. It is provided "as is" without express 24 | * or implied warranty. 25 | */ 26 | 27 | #ifndef K5_BUF_H 28 | #define K5_BUF_H 29 | 30 | #include 31 | #include 32 | 33 | /* 34 | * The k5buf module is intended to allow multi-step string construction in a 35 | * fixed or dynamic buffer without the need to check for a failure at each step 36 | * (and without aborting on malloc failure). If an allocation failure occurs 37 | * or the fixed buffer runs out of room, the buffer will be set to an error 38 | * state which can be detected with k5_buf_status. Data in a buffer is not 39 | * automatically terminated with a zero byte; call k5_buf_cstring() to use the 40 | * contents as a C string. 41 | * 42 | * k5buf structures are usually stack-allocated. Do not put k5buf structure 43 | * pointers into public APIs. It is okay to reference the data and len fields 44 | * of a buffer (they will be NULL/0 if the buffer is in an error state), but do 45 | * not change them. 46 | */ 47 | 48 | /* Buffer type values */ 49 | enum k5buftype { K5BUF_ERROR, K5BUF_FIXED, K5BUF_DYNAMIC, K5BUF_DYNAMIC_ZAP }; 50 | 51 | struct k5buf { 52 | enum k5buftype buftype; 53 | void *data; 54 | size_t space; 55 | size_t len; 56 | }; 57 | 58 | #define EMPTY_K5BUF { K5BUF_ERROR } 59 | 60 | /* Initialize a k5buf using a fixed-sized, existing buffer. SPACE must be 61 | * more than zero, or an assertion failure will result. */ 62 | void k5_buf_init_fixed(struct k5buf *buf, void *data, size_t space); 63 | 64 | /* Initialize a k5buf using an internally allocated dynamic buffer. */ 65 | void k5_buf_init_dynamic(struct k5buf *buf); 66 | 67 | /* Initialize a k5buf using an internally allocated dynamic buffer, zeroing 68 | * memory when reallocating or freeing. */ 69 | void k5_buf_init_dynamic_zap(struct k5buf *buf); 70 | 71 | /* Add a C string to BUF. */ 72 | void k5_buf_add(struct k5buf *buf, const char *data); 73 | 74 | /* Add a counted series of bytes to BUF. */ 75 | void k5_buf_add_len(struct k5buf *buf, const void *data, size_t len); 76 | 77 | /* Add sprintf-style formatted data to BUF. For a fixed-length buffer this 78 | * operation will fail if there isn't room for a zero terminator. */ 79 | void k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...) 80 | #if !defined(__cplusplus) && (__GNUC__ > 2) 81 | __attribute__((__format__(__printf__, 2, 3))) 82 | #endif 83 | ; 84 | 85 | /* Add sprintf-style formatted data to BUF, with a va_list. The value of ap is 86 | * undefined after the call. */ 87 | void k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap) 88 | #if !defined(__cplusplus) && (__GNUC__ > 2) 89 | __attribute__((__format__(__printf__, 2, 0))) 90 | #endif 91 | ; 92 | 93 | /* Without changing the length of buf, ensure that there is a zero byte after 94 | * buf.data and return it. Return NULL on error. */ 95 | char *k5_buf_cstring(struct k5buf *buf); 96 | 97 | /* Extend the length of buf by len and return a pointer to the reserved space, 98 | * to be filled in by the caller. Return NULL on error. */ 99 | void *k5_buf_get_space(struct k5buf *buf, size_t len); 100 | 101 | /* Truncate BUF. LEN must be between 0 and the existing buffer 102 | * length, or an assertion failure will result. */ 103 | void k5_buf_truncate(struct k5buf *buf, size_t len); 104 | 105 | /* Return ENOMEM if buf is in an error state, 0 otherwise. */ 106 | int k5_buf_status(struct k5buf *buf); 107 | 108 | /* 109 | * Free the storage used in the dynamic buffer BUF. The caller may choose to 110 | * take responsibility for freeing the data pointer instead of using this 111 | * function. If BUF is a fixed buffer, an assertion failure will result. 112 | * Freeing a buffer in the error state, a buffer initialized with EMPTY_K5BUF, 113 | * or a zeroed k5buf structure is a no-op. 114 | */ 115 | void k5_buf_free(struct k5buf *buf); 116 | 117 | static inline void 118 | k5_buf_add_byte(struct k5buf *buf, uint8_t val) 119 | { 120 | k5_buf_add_len(buf, &val, 1); 121 | } 122 | 123 | static inline void 124 | k5_buf_add_uint16_be(struct k5buf *buf, uint16_t val) 125 | { 126 | void *p = k5_buf_get_space(buf, 2); 127 | 128 | if (p != NULL) 129 | store_16_be(val, p); 130 | } 131 | 132 | static inline void 133 | k5_buf_add_uint16_le(struct k5buf *buf, uint16_t val) 134 | { 135 | void *p = k5_buf_get_space(buf, 2); 136 | 137 | if (p != NULL) 138 | store_16_le(val, p); 139 | } 140 | 141 | static inline void 142 | k5_buf_add_uint32_be(struct k5buf *buf, uint32_t val) 143 | { 144 | void *p = k5_buf_get_space(buf, 4); 145 | 146 | if (p != NULL) 147 | store_32_be(val, p); 148 | } 149 | 150 | static inline void 151 | k5_buf_add_uint32_le(struct k5buf *buf, uint32_t val) 152 | { 153 | void *p = k5_buf_get_space(buf, 4); 154 | 155 | if (p != NULL) 156 | store_32_le(val, p); 157 | } 158 | 159 | static inline void 160 | k5_buf_add_uint64_be(struct k5buf *buf, uint64_t val) 161 | { 162 | void *p = k5_buf_get_space(buf, 8); 163 | 164 | if (p != NULL) 165 | store_64_be(val, p); 166 | } 167 | 168 | static inline void 169 | k5_buf_add_uint64_le(struct k5buf *buf, uint64_t val) 170 | { 171 | void *p = k5_buf_get_space(buf, 8); 172 | 173 | if (p != NULL) 174 | store_64_le(val, p); 175 | } 176 | 177 | #endif /* K5_BUF_H */ 178 | -------------------------------------------------------------------------------- /auth/kinit_client/k5-err.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* include/k5-err.h */ 3 | /* 4 | * Copyright 2006, 2007 Massachusetts Institute of Technology. 5 | * All Rights Reserved. 6 | * 7 | * Export of this software from the United States of America may 8 | * require a specific license from the United States Government. 9 | * It is the responsibility of any person or organization contemplating 10 | * export to obtain such a license before exporting. 11 | * 12 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 | * distribute this software and its documentation for any purpose and 14 | * without fee is hereby granted, provided that the above copyright 15 | * notice appear in all copies and that both that copyright notice and 16 | * this permission notice appear in supporting documentation, and that 17 | * the name of M.I.T. not be used in advertising or publicity pertaining 18 | * to distribution of the software without specific, written prior 19 | * permission. Furthermore if you modify this software you must label 20 | * your software as modified software and not distribute it in such a 21 | * fashion that it might be confused with the original M.I.T. software. 22 | * M.I.T. makes no representations about the suitability of 23 | * this software for any purpose. It is provided "as is" without express 24 | * or implied warranty. 25 | */ 26 | 27 | /* 28 | * 29 | * Error-message handling 30 | */ 31 | 32 | #ifndef K5_ERR_H 33 | #define K5_ERR_H 34 | 35 | #if defined(_MSDOS) || defined(_WIN32) 36 | #include 37 | #endif 38 | #ifndef KRB5_CALLCONV 39 | #define KRB5_CALLCONV 40 | #define KRB5_CALLCONV_C 41 | #endif 42 | 43 | #include 44 | 45 | struct errinfo { 46 | long code; 47 | char *msg; 48 | }; 49 | #define EMPTY_ERRINFO { 0, NULL } 50 | 51 | void k5_set_error(struct errinfo *ep, long code, const char *fmt, ...) 52 | #if !defined(__cplusplus) && (__GNUC__ > 2) 53 | __attribute__((__format__(__printf__, 3, 4))) 54 | #endif 55 | ; 56 | 57 | void k5_vset_error(struct errinfo *ep, long code, const char *fmt, 58 | va_list args) 59 | #if !defined(__cplusplus) && (__GNUC__ > 2) 60 | __attribute__((__format__(__printf__, 3, 0))) 61 | #endif 62 | ; 63 | 64 | const char *k5_get_error(struct errinfo *ep, long code); 65 | void k5_free_error(struct errinfo *ep, const char *msg); 66 | void k5_clear_error(struct errinfo *ep); 67 | void k5_set_error_info_callout_fn(const char *(KRB5_CALLCONV *f)(long)); 68 | 69 | #endif /* K5_ERR_H */ 70 | -------------------------------------------------------------------------------- /auth/kinit_client/k5-gmt_mktime.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* include/k5-gmt_mktime.h */ 3 | /* 4 | * Copyright 2008 Massachusetts Institute of Technology. 5 | * All Rights Reserved. 6 | * 7 | * Export of this software from the United States of America may 8 | * require a specific license from the United States Government. 9 | * It is the responsibility of any person or organization contemplating 10 | * export to obtain such a license before exporting. 11 | * 12 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 | * distribute this software and its documentation for any purpose and 14 | * without fee is hereby granted, provided that the above copyright 15 | * notice appear in all copies and that both that copyright notice and 16 | * this permission notice appear in supporting documentation, and that 17 | * the name of M.I.T. not be used in advertising or publicity pertaining 18 | * to distribution of the software without specific, written prior 19 | * permission. Furthermore if you modify this software you must label 20 | * your software as modified software and not distribute it in such a 21 | * fashion that it might be confused with the original M.I.T. software. 22 | * M.I.T. makes no representations about the suitability of 23 | * this software for any purpose. It is provided "as is" without express 24 | * or implied warranty. 25 | */ 26 | 27 | /* 28 | * 29 | * GMT struct tm conversion 30 | * 31 | * Because of ordering of things in the UNIX build, we can't just keep 32 | * the declaration in k5-int.h and include it in 33 | * util/support/gmt_mktime.c, since k5-int.h includes krb5.h which 34 | * hasn't been built when gmt_mktime.c gets compiled. Hence this 35 | * silly little helper header. 36 | */ 37 | 38 | #ifndef K5_GMT_MKTIME_H 39 | #define K5_GMT_MKTIME_H 40 | 41 | time_t krb5int_gmt_mktime (struct tm *); 42 | 43 | #endif /* K5_GMT_MKTIME_H */ 44 | -------------------------------------------------------------------------------- /auth/kinit_client/k5-int-pkinit.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * COPYRIGHT (C) 2006 4 | * THE REGENTS OF THE UNIVERSITY OF MICHIGAN 5 | * ALL RIGHTS RESERVED 6 | * 7 | * Permission is granted to use, copy, create derivative works 8 | * and redistribute this software and such derivative works 9 | * for any purpose, so long as the name of The University of 10 | * Michigan is not used in any advertising or publicity 11 | * pertaining to the use of distribution of this software 12 | * without specific, written prior authorization. If the 13 | * above copyright notice or any other identification of the 14 | * University of Michigan is included in any copy of any 15 | * portion of this software, then the disclaimer below must 16 | * also be included. 17 | * 18 | * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION 19 | * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY 20 | * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF 21 | * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 22 | * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF 23 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 24 | * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE 25 | * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 26 | * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING 27 | * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN 28 | * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGES. 30 | */ 31 | 32 | #ifndef _KRB5_INT_PKINIT_H 33 | #define _KRB5_INT_PKINIT_H 34 | 35 | /* 36 | * pkinit structures 37 | */ 38 | 39 | /* PKAuthenticator */ 40 | typedef struct _krb5_pk_authenticator { 41 | krb5_int32 cusec; /* (0..999999) */ 42 | krb5_timestamp ctime; 43 | krb5_int32 nonce; /* (0..4294967295) */ 44 | krb5_checksum paChecksum; 45 | krb5_data *freshnessToken; 46 | } krb5_pk_authenticator; 47 | 48 | /* AlgorithmIdentifier */ 49 | typedef struct _krb5_algorithm_identifier { 50 | krb5_data algorithm; /* OID */ 51 | krb5_data parameters; /* Optional */ 52 | } krb5_algorithm_identifier; 53 | 54 | /** AuthPack from RFC 4556*/ 55 | typedef struct _krb5_auth_pack { 56 | krb5_pk_authenticator pkAuthenticator; 57 | krb5_data clientPublicValue; /* Optional */ 58 | krb5_algorithm_identifier **supportedCMSTypes; /* Optional */ 59 | krb5_data clientDHNonce; /* Optional */ 60 | krb5_data **supportedKDFs; /* OIDs of KDFs; OPTIONAL */ 61 | } krb5_auth_pack; 62 | 63 | /* ExternalPrincipalIdentifier */ 64 | typedef struct _krb5_external_principal_identifier { 65 | krb5_data subjectName; /* Optional */ 66 | krb5_data issuerAndSerialNumber; /* Optional */ 67 | krb5_data subjectKeyIdentifier; /* Optional */ 68 | } krb5_external_principal_identifier; 69 | 70 | /* PA-PK-AS-REQ (rfc4556 -- PA TYPE 16) */ 71 | typedef struct _krb5_pa_pk_as_req { 72 | krb5_data signedAuthPack; 73 | krb5_external_principal_identifier **trustedCertifiers; /* Optional array */ 74 | krb5_data kdcPkId; /* Optional */ 75 | } krb5_pa_pk_as_req; 76 | 77 | /** Pkinit DHRepInfo */ 78 | typedef struct _krb5_dh_rep_info { 79 | krb5_data dhSignedData; 80 | krb5_data serverDHNonce; /* Optional */ 81 | krb5_data *kdfID; /* OID of selected KDF OPTIONAL */ 82 | } krb5_dh_rep_info; 83 | 84 | /* KDCDHKeyInfo */ 85 | typedef struct _krb5_kdc_dh_key_info { 86 | krb5_data subjectPublicKey; /* BIT STRING */ 87 | krb5_int32 nonce; /* (0..4294967295) */ 88 | krb5_timestamp dhKeyExpiration; /* Optional */ 89 | } krb5_kdc_dh_key_info; 90 | 91 | /* ReplyKeyPack */ 92 | typedef struct _krb5_reply_key_pack { 93 | krb5_keyblock replyKey; 94 | krb5_checksum asChecksum; 95 | } krb5_reply_key_pack; 96 | 97 | /* PA-PK-AS-REP (rfc4556 -- PA TYPE 17) */ 98 | typedef struct _krb5_pa_pk_as_rep { 99 | enum krb5_pa_pk_as_rep_selection { 100 | choice_pa_pk_as_rep_UNKNOWN = -1, 101 | choice_pa_pk_as_rep_dhInfo = 0, 102 | choice_pa_pk_as_rep_encKeyPack = 1 103 | } choice; 104 | union krb5_pa_pk_as_rep_choices { 105 | krb5_dh_rep_info dh_Info; 106 | krb5_data encKeyPack; 107 | } u; 108 | } krb5_pa_pk_as_rep; 109 | 110 | /* SP80056A OtherInfo, for pkinit algorithm agility */ 111 | typedef struct _krb5_sp80056a_other_info { 112 | krb5_algorithm_identifier algorithm_identifier; 113 | krb5_principal party_u_info; 114 | krb5_principal party_v_info; 115 | krb5_data supp_pub_info; 116 | } krb5_sp80056a_other_info; 117 | 118 | /* PkinitSuppPubInfo, for pkinit algorithm agility */ 119 | typedef struct _krb5_pkinit_supp_pub_info { 120 | krb5_enctype enctype; 121 | krb5_data as_req; 122 | krb5_data pk_as_rep; 123 | } krb5_pkinit_supp_pub_info; 124 | 125 | /* 126 | * Begin "asn1.h" 127 | */ 128 | 129 | /************************************************************************* 130 | * Prototypes for pkinit asn.1 encode routines 131 | *************************************************************************/ 132 | 133 | krb5_error_code 134 | encode_krb5_pa_pk_as_req(const krb5_pa_pk_as_req *rep, krb5_data **code); 135 | 136 | krb5_error_code 137 | encode_krb5_pa_pk_as_rep(const krb5_pa_pk_as_rep *rep, krb5_data **code); 138 | 139 | krb5_error_code 140 | encode_krb5_auth_pack(const krb5_auth_pack *rep, krb5_data **code); 141 | 142 | krb5_error_code 143 | encode_krb5_kdc_dh_key_info(const krb5_kdc_dh_key_info *rep, krb5_data **code); 144 | 145 | krb5_error_code 146 | encode_krb5_reply_key_pack(const krb5_reply_key_pack *, krb5_data **code); 147 | 148 | krb5_error_code 149 | encode_krb5_td_trusted_certifiers(krb5_external_principal_identifier *const *, 150 | krb5_data **code); 151 | 152 | krb5_error_code 153 | encode_krb5_td_dh_parameters(krb5_algorithm_identifier *const *, 154 | krb5_data **code); 155 | 156 | krb5_error_code 157 | encode_krb5_sp80056a_other_info(const krb5_sp80056a_other_info *, 158 | krb5_data **); 159 | 160 | krb5_error_code 161 | encode_krb5_pkinit_supp_pub_info(const krb5_pkinit_supp_pub_info *, 162 | krb5_data **); 163 | 164 | /************************************************************************* 165 | * Prototypes for pkinit asn.1 decode routines 166 | *************************************************************************/ 167 | 168 | krb5_error_code 169 | decode_krb5_pa_pk_as_req(const krb5_data *, krb5_pa_pk_as_req **); 170 | 171 | krb5_error_code 172 | decode_krb5_pa_pk_as_rep(const krb5_data *, krb5_pa_pk_as_rep **); 173 | 174 | krb5_error_code 175 | decode_krb5_auth_pack(const krb5_data *, krb5_auth_pack **); 176 | 177 | krb5_error_code 178 | decode_krb5_kdc_dh_key_info(const krb5_data *, krb5_kdc_dh_key_info **); 179 | 180 | krb5_error_code 181 | decode_krb5_principal_name(const krb5_data *, krb5_principal_data **); 182 | 183 | krb5_error_code 184 | decode_krb5_reply_key_pack(const krb5_data *, krb5_reply_key_pack **); 185 | 186 | krb5_error_code 187 | decode_krb5_td_trusted_certifiers(const krb5_data *, 188 | krb5_external_principal_identifier ***); 189 | 190 | krb5_error_code 191 | decode_krb5_td_dh_parameters(const krb5_data *, krb5_algorithm_identifier ***); 192 | 193 | krb5_error_code 194 | encode_krb5_enc_data(const krb5_enc_data *, krb5_data **); 195 | 196 | krb5_error_code 197 | encode_krb5_encryption_key(const krb5_keyblock *rep, krb5_data **code); 198 | 199 | krb5_error_code 200 | krb5_encrypt_helper(krb5_context context, const krb5_keyblock *key, 201 | krb5_keyusage keyusage, const krb5_data *plain, 202 | krb5_enc_data *cipher); 203 | 204 | #endif /* _KRB5_INT_PKINIT_H */ 205 | -------------------------------------------------------------------------------- /auth/kinit_client/k5-plugin.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright (C) 2006 Massachusetts Institute of Technology. 4 | * All Rights Reserved. 5 | * 6 | * This software is being provided to you, the LICENSEE, by the 7 | * Massachusetts Institute of Technology (M.I.T.) under the following 8 | * license. By obtaining, using and/or copying this software, you agree 9 | * that you have read, understood, and will comply with these terms and 10 | * conditions: 11 | * 12 | * Export of this software from the United States of America may 13 | * require a specific license from the United States Government. 14 | * It is the responsibility of any person or organization contemplating 15 | * export to obtain such a license before exporting. 16 | * 17 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute 18 | * this software and its documentation for any purpose and without fee or 19 | * royalty is hereby granted, provided that you agree to comply with the 20 | * following copyright notice and statements, including the disclaimer, and 21 | * that the same appear on ALL copies of the software and documentation, 22 | * including modifications that you make for internal use or for 23 | * distribution: 24 | * 25 | * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS 26 | * OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not 27 | * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF 28 | * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF 29 | * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY 30 | * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 31 | * 32 | * The name of the Massachusetts Institute of Technology or M.I.T. may NOT 33 | * be used in advertising or publicity pertaining to distribution of the 34 | * software. Title to copyright in this software and any associated 35 | * documentation shall at all times remain with M.I.T., and USER agrees to 36 | * preserve same. 37 | * 38 | * Furthermore if you modify this software you must label 39 | * your software as modified software and not distribute it in such a 40 | * fashion that it might be confused with the original M.I.T. software. 41 | */ 42 | 43 | /* Just those definitions which are needed by util/support/plugins.c, 44 | which gets compiled before util/et is built, which happens before 45 | we can construct krb5.h, which is included by k5-int.h. 46 | 47 | So, no krb5 types. */ 48 | 49 | #ifndef K5_PLUGIN_H 50 | #define K5_PLUGIN_H 51 | 52 | #if defined(_MSDOS) || defined(_WIN32) 53 | #include "win-mac.h" 54 | #endif 55 | #include "autoconf.h" 56 | #ifndef KRB5_CALLCONV 57 | #define KRB5_CALLCONV 58 | #define KRB5_CALLCONV_C 59 | #endif 60 | 61 | #include "k5-err.h" 62 | 63 | /* 64 | * Plugins normally export fixed symbol names, but when statically 65 | * linking plugins, we need a different symbol name for each plugin. 66 | * The first argument to PLUGIN_SYMBOL_NAME acts as the 67 | * differentiator, and is only used for static plugin linking. 68 | * 69 | * Although this macro (and thus this header file) are used in plugins 70 | * whose code lies inside the krb5 tree, plugins maintained separately 71 | * from the krb5 tree do not need it; they can just use the fixed 72 | * symbol name unconditionally. 73 | */ 74 | #ifdef STATIC_PLUGINS 75 | #define PLUGIN_SYMBOL_NAME(prefix, symbol) prefix ## _ ## symbol 76 | #else 77 | #define PLUGIN_SYMBOL_NAME(prefix, symbol) symbol 78 | #endif 79 | 80 | struct plugin_file_handle; /* opaque */ 81 | 82 | struct plugin_dir_handle { 83 | /* This points to a NULL-terminated list of pointers to plugin_file_handle structs */ 84 | struct plugin_file_handle **files; 85 | }; 86 | #define PLUGIN_DIR_INIT(P) ((P)->files = NULL) 87 | #define PLUGIN_DIR_OPEN(P) ((P)->files != NULL) 88 | 89 | long KRB5_CALLCONV 90 | krb5int_open_plugin (const char *, struct plugin_file_handle **, struct errinfo *); 91 | void KRB5_CALLCONV 92 | krb5int_close_plugin (struct plugin_file_handle *); 93 | 94 | long KRB5_CALLCONV 95 | krb5int_get_plugin_data (struct plugin_file_handle *, const char *, void **, 96 | struct errinfo *); 97 | 98 | long KRB5_CALLCONV 99 | krb5int_get_plugin_func (struct plugin_file_handle *, const char *, 100 | void (**)(), struct errinfo *); 101 | 102 | 103 | long KRB5_CALLCONV 104 | krb5int_open_plugin_dirs (const char * const *, const char * const *, 105 | struct plugin_dir_handle *, struct errinfo *); 106 | void KRB5_CALLCONV 107 | krb5int_close_plugin_dirs (struct plugin_dir_handle *); 108 | 109 | long KRB5_CALLCONV 110 | krb5int_get_plugin_dir_data (struct plugin_dir_handle *, const char *, 111 | void ***, struct errinfo *); 112 | void KRB5_CALLCONV 113 | krb5int_free_plugin_dir_data (void **); 114 | 115 | long KRB5_CALLCONV 116 | krb5int_get_plugin_dir_func (struct plugin_dir_handle *, const char *, 117 | void (***)(void), struct errinfo *); 118 | void KRB5_CALLCONV 119 | krb5int_free_plugin_dir_func (void (**)(void)); 120 | 121 | #endif /* K5_PLUGIN_H */ 122 | -------------------------------------------------------------------------------- /auth/kinit_client/kinit_kdb.c: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* clients/kinit/kinit_kdb.c */ 3 | /* 4 | * Copyright (C) 2010 by the Massachusetts Institute of Technology. 5 | * All rights reserved. 6 | * 7 | * Export of this software from the United States of America may 8 | * require a specific license from the United States Government. 9 | * It is the responsibility of any person or organization contemplating 10 | * export to obtain such a license before exporting. 11 | * 12 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 | * distribute this software and its documentation for any purpose and 14 | * without fee is hereby granted, provided that the above copyright 15 | * notice appear in all copies and that both that copyright notice and 16 | * this permission notice appear in supporting documentation, and that 17 | * the name of M.I.T. not be used in advertising or publicity pertaining 18 | * to distribution of the software without specific, written prior 19 | * permission. Furthermore if you modify this software you must label 20 | * your software as modified software and not distribute it in such a 21 | * fashion that it might be confused with the original M.I.T. software. 22 | * M.I.T. makes no representations about the suitability of 23 | * this software for any purpose. It is provided "as is" without express 24 | * or implied warranty. 25 | */ 26 | 27 | /** 28 | * @file kinit_kdb.c 29 | * Operations to open the KDB and make the KDB key table available 30 | * for kinit. 31 | */ 32 | 33 | 34 | #include "k5-int.h" 35 | #include 36 | #include 37 | #include "extern.h" 38 | 39 | /* Server handle */ 40 | static void *server_handle; 41 | 42 | /* Free and reinitialize *pcontext with the KDB opened to the given realm, so 43 | * that it can be used with the KDB keytab type. */ 44 | krb5_error_code 45 | kinit_kdb_init(krb5_context *pcontext, char *realm) 46 | { 47 | kadm5_config_params config; 48 | krb5_error_code ret; 49 | 50 | if (*pcontext) { 51 | krb5_free_context(*pcontext); 52 | *pcontext = NULL; 53 | } 54 | memset(&config, 0, sizeof config); 55 | 56 | ret = kadm5_init_krb5_context(pcontext); 57 | if (ret) 58 | return ret; 59 | 60 | config.mask = KADM5_CONFIG_REALM; 61 | config.realm = realm; 62 | ret = kadm5_init(*pcontext, "kinit", NULL, "kinit", &config, 63 | KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, 64 | &server_handle); 65 | if (ret) 66 | return ret; 67 | 68 | return krb5_db_register_keytab(*pcontext); 69 | } 70 | 71 | void 72 | kinit_kdb_fini() 73 | { 74 | kadm5_destroy(server_handle); 75 | } 76 | -------------------------------------------------------------------------------- /auth/kinit_client/osconf.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology. 4 | * All Rights Reserved. 5 | * 6 | * Export of this software from the United States of America may 7 | * require a specific license from the United States Government. 8 | * It is the responsibility of any person or organization contemplating 9 | * export to obtain such a license before exporting. 10 | * 11 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 | * distribute this software and its documentation for any purpose and 13 | * without fee is hereby granted, provided that the above copyright 14 | * notice appear in all copies and that both that copyright notice and 15 | * this permission notice appear in supporting documentation, and that 16 | * the name of M.I.T. not be used in advertising or publicity pertaining 17 | * to distribution of the software without specific, written prior 18 | * permission. Furthermore if you modify this software you must label 19 | * your software as modified software and not distribute it in such a 20 | * fashion that it might be confused with the original M.I.T. software. 21 | * M.I.T. makes no representations about the suitability of 22 | * this software for any purpose. It is provided "as is" without express 23 | * or implied warranty. 24 | */ 25 | 26 | /* Site- and OS- dependent configuration */ 27 | 28 | #ifndef KRB5_OSCONF__ 29 | #define KRB5_OSCONF__ 30 | 31 | #if !defined(_WIN32) 32 | /* Don't try to pull in autoconf.h for Windows, since it's not used */ 33 | #ifndef KRB5_AUTOCONF__ 34 | #define KRB5_AUTOCONF__ 35 | #include "autoconf.h" 36 | #endif 37 | #endif 38 | 39 | #if defined(__MACH__) && defined(__APPLE__) 40 | # include 41 | #endif 42 | 43 | #if defined(_WIN32) 44 | #define DEFAULT_PROFILE_FILENAME "krb5.ini" 45 | #else /* !_WINDOWS */ 46 | #if TARGET_OS_MAC 47 | #define DEFAULT_SECURE_PROFILE_PATH "/Library/Preferences/edu.mit.Kerberos:/etc/krb5.conf:/usr/local/etc/krb5.conf" 48 | #define DEFAULT_PROFILE_PATH ("~/Library/Preferences/edu.mit.Kerberos" ":" DEFAULT_SECURE_PROFILE_PATH) 49 | #define KRB5_PLUGIN_BUNDLE_DIR "/System/Library/KerberosPlugins/KerberosFrameworkPlugins" 50 | #define KDB5_PLUGIN_BUNDLE_DIR "/System/Library/KerberosPlugins/KerberosDatabasePlugins" 51 | #define KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR "/System/Library/KerberosPlugins/KerberosAuthDataPlugins" 52 | #else 53 | #define DEFAULT_SECURE_PROFILE_PATH "/etc/krb5.conf:/usr/local/etc/krb5.conf" 54 | #define DEFAULT_PROFILE_PATH DEFAULT_SECURE_PROFILE_PATH 55 | #endif 56 | #endif /* _WINDOWS */ 57 | 58 | #ifdef _WIN32 59 | #define DEFAULT_PLUGIN_BASE_DIR "%{LIBDIR}\\plugins" 60 | #else 61 | #define DEFAULT_PLUGIN_BASE_DIR "/usr/local/lib/krb5/plugins" 62 | #endif 63 | 64 | #if defined(_WIN64) 65 | #define PLUGIN_EXT "64.dll" 66 | #elif defined(_WIN32) 67 | #define PLUGIN_EXT "32.dll" 68 | #else 69 | #define PLUGIN_EXT ".so" 70 | #endif 71 | 72 | #define KDC_DIR "/usr/local/var/krb5kdc" 73 | #define KDC_RUN_DIR "/usr/local/var/run/krb5kdc" 74 | #define DEFAULT_KDB_FILE KDC_DIR "/principal" 75 | #define DEFAULT_KEYFILE_STUB KDC_DIR "/.k5." 76 | #define KRB5_DEFAULT_ADMIN_ACL KDC_DIR "/krb5_adm.acl" 77 | /* Used by old admin server */ 78 | #define DEFAULT_ADMIN_ACL KDC_DIR "/kadm_old.acl" 79 | 80 | /* Location of KDC profile */ 81 | #define DEFAULT_KDC_PROFILE KDC_DIR "/kdc.conf" 82 | #define KDC_PROFILE_ENV "KRB5_KDC_PROFILE" 83 | 84 | #if TARGET_OS_MAC 85 | #define DEFAULT_KDB_LIB_PATH { KDB5_PLUGIN_BUNDLE_DIR, "/usr/local/lib/krb5/plugins/kdb", NULL } 86 | #else 87 | #define DEFAULT_KDB_LIB_PATH { "/usr/local/lib/krb5/plugins/kdb", NULL } 88 | #endif 89 | 90 | #define DEFAULT_KDC_ENCTYPE ENCTYPE_AES256_CTS_HMAC_SHA1_96 91 | #define KDCRCACHE "dfl:krb5kdc_rcache" 92 | 93 | #define KDC_PORTNAME "kerberos" /* for /etc/services or equiv. */ 94 | 95 | #define KRB5_DEFAULT_PORT 88 96 | 97 | #define DEFAULT_KPASSWD_PORT 464 98 | 99 | #define DEFAULT_KDC_UDP_PORTLIST "88" 100 | #define DEFAULT_KDC_TCP_PORTLIST "88" 101 | #define DEFAULT_TCP_LISTEN_BACKLOG 5 102 | 103 | /* 104 | * Defaults for the KADM5 admin system. 105 | */ 106 | #define DEFAULT_KADM5_KEYTAB KDC_DIR "/kadm5.keytab" 107 | #define DEFAULT_KADM5_ACL_FILE KDC_DIR "/kadm5.acl" 108 | #define DEFAULT_KADM5_PORT 749 /* assigned by IANA */ 109 | 110 | #define KRB5_DEFAULT_SUPPORTED_ENCTYPES \ 111 | "aes256-cts-hmac-sha1-96:normal " \ 112 | "aes128-cts-hmac-sha1-96:normal" 113 | 114 | #define MAX_DGRAM_SIZE 65536 115 | 116 | #define RCTMPDIR "/var/tmp" /* directory to store replay caches */ 117 | 118 | #define KRB5_PATH_TTY "/dev/tty" 119 | #define KRB5_PATH_LOGIN "/usr/local/sbin/login.krb5" 120 | #define KRB5_PATH_RLOGIN "/usr/local/bin/rlogin" 121 | 122 | #define KRB5_ENV_CCNAME "KRB5CCNAME" 123 | 124 | /* 125 | * krb5 replica support follows 126 | */ 127 | 128 | #define KPROP_DEFAULT_FILE KDC_DIR "/replica_datatrans" 129 | #define KPROPD_DEFAULT_FILE KDC_DIR "/from_master" 130 | #define KPROPD_DEFAULT_KDB5_UTIL "/usr/local/sbin/kdb5_util" 131 | #define KPROPD_DEFAULT_KPROP "/usr/local/sbin/kprop" 132 | #define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE 133 | #define KPROPD_ACL_FILE KDC_DIR "/kpropd.acl" 134 | 135 | /* 136 | * GSS mechglue 137 | */ 138 | #define MECH_CONF "/usr/local/etc/gss/mech" 139 | #define MECH_LIB_PREFIX "/usr/local/lib/gss/" 140 | 141 | #endif /* KRB5_OSCONF__ */ 142 | -------------------------------------------------------------------------------- /auth/kinit_client/port-sockets.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #ifndef _PORT_SOCKET_H 3 | #define _PORT_SOCKET_H 4 | #if defined(_WIN32) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /* Some of our own infrastructure where the Winsock stuff was too hairy 11 | * to dump into a clean Unix program */ 12 | 13 | typedef WSABUF sg_buf; 14 | 15 | #define SG_ADVANCE(SG, N) \ 16 | ((SG)->len < (N) \ 17 | ? (abort(), 0) \ 18 | : ((SG)->buf += (N), (SG)->len -= (N), 0)) 19 | 20 | #define SG_LEN(SG) ((SG)->len + 0) 21 | #define SG_BUF(SG) ((SG)->buf + 0) 22 | #define SG_SET(SG, B, N) ((SG)->buf = (char *)(B),(SG)->len = (N)) 23 | 24 | #define SOCKET_INITIALIZE() 0 25 | #define SOCKET_CLEANUP() 26 | #define SOCKET_ERRNO (TranslatedWSAGetLastError()) 27 | #define SOCKET_SET_ERRNO(x) (TranslatedWSASetLastError(x)) 28 | #define SOCKET_NFDS(f) (0) /* select()'s first arg is ignored */ 29 | #define SOCKET_READ(fd, b, l) (recv(fd, b, l, 0)) 30 | #define SOCKET_WRITE(fd, b, l) (send(fd, b, l, 0)) 31 | #define SOCKET_CONNECT connect /* XXX */ 32 | #define SOCKET_GETSOCKNAME getsockname /* XXX */ 33 | #define SOCKET_CLOSE close /* XXX */ 34 | #define SOCKET_EINTR WSAEINTR 35 | 36 | /* 37 | * Return -1 for error or number of bytes written. TMP is a temporary 38 | * variable; must be declared by the caller, and must be used by this macro (to 39 | * avoid compiler warnings). 40 | */ 41 | /* WSASend returns 0 or SOCKET_ERROR. */ 42 | #define SOCKET_WRITEV_TEMP DWORD 43 | #define SOCKET_WRITEV(FD, SG, LEN, TMP) \ 44 | (WSASend((FD), (SG), (LEN), &(TMP), 0, 0, 0) ? \ 45 | (ssize_t)-1 : (ssize_t)(TMP)) 46 | 47 | #define SHUTDOWN_READ SD_RECEIVE 48 | #define SHUTDOWN_WRITE SD_SEND 49 | #define SHUTDOWN_BOTH SD_BOTH 50 | 51 | /* 52 | * Define any missing POSIX socket errors. This is for compatibility with 53 | * older versions of MSVC (pre-2010). 54 | */ 55 | #ifndef EINPROGRESS 56 | #define EINPROGRESS WSAEINPROGRESS 57 | #endif 58 | #ifndef EWOULDBLOCK 59 | #define EWOULDBLOCK WSAEWOULDBLOCK 60 | #endif 61 | #ifndef ECONNRESET 62 | #define ECONNRESET WSAECONNRESET 63 | #endif 64 | #ifndef ECONNABORTED 65 | #define ECONNABORTED WSAECONNABORTED 66 | #endif 67 | #ifndef ECONNREFUSED 68 | #define ECONNREFUSED WSAECONNREFUSED 69 | #endif 70 | #ifndef EHOSTUNREACH 71 | #define EHOSTUNREACH WSAEHOSTUNREACH 72 | #endif 73 | #ifndef ETIMEDOUT 74 | #define ETIMEDOUT WSAETIMEDOUT 75 | #endif 76 | 77 | /* Translate posix_error to its Winsock counterpart and set the last Winsock 78 | * error to the result. */ 79 | static __inline void TranslatedWSASetLastError(int posix_error) 80 | { 81 | int wsa_error; 82 | switch (posix_error) { 83 | case 0: 84 | wsa_error = 0; break; 85 | case EINPROGRESS: 86 | wsa_error = WSAEINPROGRESS; break; 87 | case EWOULDBLOCK: 88 | wsa_error = WSAEWOULDBLOCK; break; 89 | case ECONNRESET: 90 | wsa_error = WSAECONNRESET; break; 91 | case ECONNABORTED: 92 | wsa_error = WSAECONNABORTED; break; 93 | case ECONNREFUSED: 94 | wsa_error = WSAECONNREFUSED; break; 95 | case EHOSTUNREACH: 96 | wsa_error = WSAEHOSTUNREACH; break; 97 | case ETIMEDOUT: 98 | wsa_error = WSAETIMEDOUT; break; 99 | case EAFNOSUPPORT: 100 | wsa_error = WSAEAFNOSUPPORT; break; 101 | case EINVAL: 102 | wsa_error = WSAEINVAL; break; 103 | default: 104 | /* Ideally, we would log via k5-trace here, but we have no context. */ 105 | wsa_error = WSAEINVAL; break; 106 | } 107 | WSASetLastError(wsa_error); 108 | } 109 | 110 | /* 111 | * Translate Winsock errors to their POSIX counterparts. This is necessary for 112 | * MSVC 2010+, where both Winsock and POSIX errors are defined. 113 | */ 114 | static __inline int TranslatedWSAGetLastError() 115 | { 116 | int err = WSAGetLastError(); 117 | switch (err) { 118 | case 0: 119 | break; 120 | case WSAEINPROGRESS: 121 | err = EINPROGRESS; break; 122 | case WSAEWOULDBLOCK: 123 | err = EWOULDBLOCK; break; 124 | case WSAECONNRESET: 125 | err = ECONNRESET; break; 126 | case WSAECONNABORTED: 127 | err = ECONNABORTED; break; 128 | case WSAECONNREFUSED: 129 | err = ECONNREFUSED; break; 130 | case WSAEHOSTUNREACH: 131 | err = EHOSTUNREACH; break; 132 | case WSAETIMEDOUT: 133 | err = ETIMEDOUT; break; 134 | case WSAEAFNOSUPPORT: 135 | err = EAFNOSUPPORT; break; 136 | case WSAEINVAL: 137 | err = EINVAL; break; 138 | default: 139 | /* Ideally, we would log via k5-trace here, but we have no context. */ 140 | err = EINVAL; break; 141 | } 142 | return err; 143 | } 144 | 145 | #elif defined(__palmos__) 146 | 147 | /* If this source file requires it, define struct sockaddr_in (and possibly 148 | * other things related to network I/O). */ 149 | 150 | #include "autoconf.h" 151 | #include 152 | typedef int socklen_t; 153 | 154 | #else /* UNIX variants */ 155 | 156 | #include "autoconf.h" 157 | 158 | #include 159 | #include /* For struct sockaddr_in and in_addr */ 160 | #include /* For inet_ntoa */ 161 | #include 162 | #include /* For memset */ 163 | 164 | #ifndef HAVE_NETDB_H_H_ERRNO 165 | extern int h_errno; /* In case it's missing, e.g., HP-UX 10.20. */ 166 | #endif 167 | 168 | #include /* For MAXHOSTNAMELEN */ 169 | #include /* For SOCK_*, AF_*, etc */ 170 | #include /* For struct timeval */ 171 | #include /* For struct ifconf, for localaddr.c */ 172 | #ifdef HAVE_SYS_UIO_H 173 | #include /* For struct iovec, for sg_buf */ 174 | #endif 175 | #ifdef HAVE_SYS_FILIO_H 176 | #include /* For FIONBIO on Solaris. */ 177 | #endif 178 | 179 | /* 180 | * Either size_t or int or unsigned int is probably right. Under 181 | * SunOS 4, it looks like int is desired, according to the accept man 182 | * page. 183 | */ 184 | #ifndef HAVE_SOCKLEN_T 185 | typedef int socklen_t; 186 | #endif 187 | 188 | #ifndef HAVE_STRUCT_SOCKADDR_STORAGE 189 | struct krb5int_sockaddr_storage { 190 | struct sockaddr_in s; 191 | /* Plenty of slop just in case we get an ipv6 address anyways. */ 192 | long extra[16]; 193 | }; 194 | #define sockaddr_storage krb5int_sockaddr_storage 195 | #endif 196 | 197 | /* Unix equivalents of Winsock calls */ 198 | #define SOCKET int 199 | #define INVALID_SOCKET ((SOCKET)~0) 200 | #define closesocket close 201 | #define ioctlsocket ioctl 202 | #define SOCKET_ERROR (-1) 203 | 204 | typedef struct iovec sg_buf; 205 | 206 | #define SG_ADVANCE(SG, N) \ 207 | ((SG)->iov_len < (N) \ 208 | ? (abort(), 0) \ 209 | : ((SG)->iov_base = (char *) (SG)->iov_base + (N), \ 210 | (SG)->iov_len -= (N), 0)) 211 | 212 | #define SG_LEN(SG) ((SG)->iov_len + 0) 213 | #define SG_BUF(SG) ((char*)(SG)->iov_base + 0) 214 | #define SG_SET(SG, B, L) ((SG)->iov_base = (char*)(B), (SG)->iov_len = (L)) 215 | 216 | #define SOCKET_INITIALIZE() (0) /* No error (or anything else) */ 217 | #define SOCKET_CLEANUP() /* nothing */ 218 | #define SOCKET_ERRNO errno 219 | #define SOCKET_SET_ERRNO(x) (errno = (x)) 220 | #define SOCKET_NFDS(f) ((f)+1) /* select() arg for a single fd */ 221 | #define SOCKET_READ read 222 | #define SOCKET_WRITE write 223 | static inline int 224 | socket_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) 225 | { 226 | int st; 227 | #ifdef SO_NOSIGPIPE 228 | int set = 1; 229 | #endif 230 | 231 | st = connect(fd, addr, addrlen); 232 | if (st == -1) 233 | return st; 234 | 235 | #ifdef SO_NOSIGPIPE 236 | st = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(set)); 237 | if (st != 0) 238 | st = -1; 239 | #endif 240 | 241 | return st; 242 | } 243 | #define SOCKET_CONNECT socket_connect 244 | #define SOCKET_GETSOCKNAME getsockname 245 | #define SOCKET_CLOSE close 246 | #define SOCKET_EINTR EINTR 247 | #define SOCKET_WRITEV_TEMP int 248 | static inline ssize_t 249 | socket_sendmsg(SOCKET fd, sg_buf *iov, int iovcnt) 250 | { 251 | struct msghdr msg; 252 | int flags = 0; 253 | 254 | #ifdef MSG_NOSIGNAL 255 | flags |= MSG_NOSIGNAL; 256 | #endif 257 | 258 | memset(&msg, 0, sizeof(msg)); 259 | msg.msg_iov = iov; 260 | msg.msg_iovlen = iovcnt; 261 | 262 | return sendmsg(fd, &msg, flags); 263 | } 264 | /* Use TMP to avoid compiler warnings and keep things consistent with 265 | * Windows version. */ 266 | #define SOCKET_WRITEV(FD, SG, LEN, TMP) \ 267 | ((TMP) = socket_sendmsg((FD), (SG), (LEN)), (TMP)) 268 | 269 | #define SHUTDOWN_READ 0 270 | #define SHUTDOWN_WRITE 1 271 | #define SHUTDOWN_BOTH 2 272 | 273 | #ifndef HAVE_INET_NTOP 274 | #define inet_ntop(AF,SRC,DST,CNT) \ 275 | ((AF) == AF_INET \ 276 | ? ((CNT) < 16 \ 277 | ? (SOCKET_SET_ERRNO(ENOSPC), (const char *)NULL) \ 278 | : (sprintf((DST), "%d.%d.%d.%d", \ 279 | ((const unsigned char *)(const void *)(SRC))[0] & 0xff, \ 280 | ((const unsigned char *)(const void *)(SRC))[1] & 0xff, \ 281 | ((const unsigned char *)(const void *)(SRC))[2] & 0xff, \ 282 | ((const unsigned char *)(const void *)(SRC))[3] & 0xff), \ 283 | (DST))) \ 284 | : (SOCKET_SET_ERRNO(EAFNOSUPPORT), (const char *)NULL)) 285 | #define HAVE_INET_NTOP 286 | #endif 287 | 288 | #endif /* _WIN32 */ 289 | 290 | #if !defined(_WIN32) 291 | /* UNIX or ...? */ 292 | # ifdef S_SPLINT_S 293 | extern int socket (int, int, int) /*@*/; 294 | # endif 295 | #endif 296 | 297 | #endif /*_PORT_SOCKET_H*/ 298 | -------------------------------------------------------------------------------- /auth/kinit_client/socket-utils.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright (C) 2001,2005 by the Massachusetts Institute of Technology, 4 | * Cambridge, MA, USA. All Rights Reserved. 5 | * 6 | * This software is being provided to you, the LICENSEE, by the 7 | * Massachusetts Institute of Technology (M.I.T.) under the following 8 | * license. By obtaining, using and/or copying this software, you agree 9 | * that you have read, understood, and will comply with these terms and 10 | * conditions: 11 | * 12 | * Export of this software from the United States of America may 13 | * require a specific license from the United States Government. 14 | * It is the responsibility of any person or organization contemplating 15 | * export to obtain such a license before exporting. 16 | * 17 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute 18 | * this software and its documentation for any purpose and without fee or 19 | * royalty is hereby granted, provided that you agree to comply with the 20 | * following copyright notice and statements, including the disclaimer, and 21 | * that the same appear on ALL copies of the software and documentation, 22 | * including modifications that you make for internal use or for 23 | * distribution: 24 | * 25 | * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS 26 | * OR WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not 27 | * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF 28 | * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF 29 | * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY 30 | * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 31 | * 32 | * The name of the Massachusetts Institute of Technology or M.I.T. may NOT 33 | * be used in advertising or publicity pertaining to distribution of the 34 | * software. Title to copyright in this software and any associated 35 | * documentation shall at all times remain with M.I.T., and USER agrees to 36 | * preserve same. 37 | * 38 | * Furthermore if you modify this software you must label 39 | * your software as modified software and not distribute it in such a 40 | * fashion that it might be confused with the original M.I.T. software. 41 | */ 42 | 43 | #ifndef SOCKET_UTILS_H 44 | #define SOCKET_UTILS_H 45 | 46 | /* Some useful stuff cross-platform for manipulating socket addresses. 47 | We assume at least ipv4 sockaddr_in support. The sockaddr_storage 48 | stuff comes from the ipv6 socket api enhancements; socklen_t is 49 | provided on some systems; the rest is just convenience for internal 50 | use in the krb5 tree. 51 | 52 | Do NOT install this file. */ 53 | 54 | /* for HAVE_SOCKLEN_T etc */ 55 | #include "autoconf.h" 56 | /* for sockaddr_storage */ 57 | #include "port-sockets.h" 58 | /* for "inline" if needed */ 59 | #include "k5-platform.h" 60 | 61 | /* 62 | * There's a lot of confusion between pointers to different sockaddr 63 | * types, and pointers with different degrees of indirection, as in 64 | * the locate_kdc type functions. Use these function to ensure we 65 | * don't do something silly like cast a "sockaddr **" to a 66 | * "sockaddr_in *". 67 | * 68 | * The casts to (void *) are to get GCC to shut up about alignment 69 | * increasing. 70 | */ 71 | static inline struct sockaddr_in *sa2sin (struct sockaddr *sa) 72 | { 73 | return (struct sockaddr_in *) (void *) sa; 74 | } 75 | static inline struct sockaddr_in6 *sa2sin6 (struct sockaddr *sa) 76 | { 77 | return (struct sockaddr_in6 *) (void *) sa; 78 | } 79 | static inline struct sockaddr *ss2sa (struct sockaddr_storage *ss) 80 | { 81 | return (struct sockaddr *) ss; 82 | } 83 | static inline struct sockaddr_in *ss2sin (struct sockaddr_storage *ss) 84 | { 85 | return (struct sockaddr_in *) ss; 86 | } 87 | static inline struct sockaddr_in6 *ss2sin6 (struct sockaddr_storage *ss) 88 | { 89 | return (struct sockaddr_in6 *) ss; 90 | } 91 | 92 | /* Set the IPv4 or IPv6 port on sa to port. Do nothing if sa is not an 93 | * Internet socket. */ 94 | static inline void 95 | sa_setport(struct sockaddr *sa, uint16_t port) 96 | { 97 | if (sa->sa_family == AF_INET) 98 | sa2sin(sa)->sin_port = htons(port); 99 | else if (sa->sa_family == AF_INET6) 100 | sa2sin6(sa)->sin6_port = htons(port); 101 | } 102 | 103 | /* Get the Internet port number of sa, or 0 if it is not an Internet socket. */ 104 | static inline uint16_t 105 | sa_getport(struct sockaddr *sa) 106 | { 107 | if (sa->sa_family == AF_INET) 108 | return ntohs(sa2sin(sa)->sin_port); 109 | else if (sa->sa_family == AF_INET6) 110 | return ntohs(sa2sin6(sa)->sin6_port); 111 | else 112 | return 0; 113 | } 114 | 115 | /* Return true if sa is an IPv4 or IPv6 socket address. */ 116 | static inline int 117 | sa_is_inet(struct sockaddr *sa) 118 | { 119 | return sa->sa_family == AF_INET || sa->sa_family == AF_INET6; 120 | } 121 | 122 | /* Return true if sa is an IPv4 or IPv6 wildcard address. */ 123 | static inline int 124 | sa_is_wildcard(struct sockaddr *sa) 125 | { 126 | if (sa->sa_family == AF_INET6) 127 | return IN6_IS_ADDR_UNSPECIFIED(&sa2sin6(sa)->sin6_addr); 128 | else if (sa->sa_family == AF_INET) 129 | return sa2sin(sa)->sin_addr.s_addr == INADDR_ANY; 130 | return 0; 131 | } 132 | 133 | /* Return the length of an IPv4 or IPv6 socket structure; abort if it is 134 | * neither. */ 135 | static inline socklen_t 136 | sa_socklen(struct sockaddr *sa) 137 | { 138 | if (sa->sa_family == AF_INET6) 139 | return sizeof(struct sockaddr_in6); 140 | else if (sa->sa_family == AF_INET) 141 | return sizeof(struct sockaddr_in); 142 | else 143 | abort(); 144 | } 145 | 146 | #endif /* SOCKET_UTILS_H */ 147 | -------------------------------------------------------------------------------- /cdk/Docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/mssql-tools:latest 2 | 3 | RUN apt-get update 4 | 5 | RUN apt-get install krb5-user -y 6 | 7 | # Install OpenSSH server 8 | RUN apt-get update && \ 9 | apt-get install -y openssh-server && \ 10 | mkdir /var/run/sshd && \ 11 | echo 'root:Qn:51eJsORJNL@~{HY@?' | chpasswd && \ 12 | sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config 13 | 14 | # SSH login fix. Otherwise user is kicked off after login 15 | RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 16 | 17 | ENV NOTVISIBLE "in users profile" 18 | RUN echo "export VISIBLE=now" >> /etc/profile 19 | 20 | EXPOSE 22 21 | 22 | RUN cat < /entrypoint.sh 23 | #!/bin/bash 24 | /usr/sbin/sshd -D 25 | EOF 26 | 27 | RUN chmod +x /entrypoint.sh 28 | 29 | ENTRYPOINT ["/entrypoint.sh"] 30 | -------------------------------------------------------------------------------- /cdk/Docker/build.sh: -------------------------------------------------------------------------------- 1 | AWS_ACCOUNT_NUMBER= 2 | REGION= 3 | 4 | docker build -t mssql-tools . 5 | 6 | ECR_LOGIN="$AWS_ACCOUNT_NUMBER".dkr.ecr."$REGION".amazonaws.com 7 | aws ecr get-login-password --region us-west-1 | docker login --username AWS --password-stdin $ECR_LOCATION 8 | 9 | TAG=$(docker images | grep "^mssql-tools" | awk '{print $3}') 10 | 11 | ECR_LOCATION="$AWS_ACCOUNT_NUMBER".dkr.ecr."$REGION".amazonaws.com/my-mssql-tools:latest 12 | docker tag "$TAG" "$ECR_LOCATION" 13 | docker push "$ECR_LOCATION" 14 | -------------------------------------------------------------------------------- /cdk/ECS_task_definition/CredentialsfetcherADStackCredentialsFetcherTaskDefinitiontemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "taskDefinitionArn": "arn:aws:ecs:us-west-1:AWS_ACCOUNT_NUMBER:task-definition/CredentialsfetcherADStackCredentialsFetcherTaskDefinitiontemplate", 3 | "containerDefinitions": [ 4 | { 5 | "name": "mssql-tools", 6 | "image": "AWS_ACCOUNT_NUMBER.dkr.ecr.us-west-1.amazonaws.com/my-mssql-tools:latest", 7 | "cpu": 0, 8 | "memoryReservation": 128, 9 | "portMappings": [], 10 | "essential": true, 11 | "environment": [], 12 | "mountPoints": [], 13 | "volumesFrom": [], 14 | "startTimeout": 120, 15 | "stopTimeout": 60, 16 | "dockerLabels": {}, 17 | "systemControls": [], 18 | "credentialSpecs": [ 19 | "credentialspecdomainless:arn:aws:s3:::S3_CREDSPEC_LOCATION" 20 | ] 21 | } 22 | ], 23 | "family": "CredentialsfetcherADStackCredentialsFetcherTaskDefinitiontemplate", 24 | "taskRoleArn": "arn:aws:iam::AWS_ACCOUNT_NUMBER:role/CredentialsFetcher-ECSTaskExecutionRolegMSA", 25 | "executionRoleArn": "arn:aws:iam::AWS_ACCOUNT_NUMBER:role/CredentialsFetcher-ECSTaskExecutionRolegMSA", 26 | "networkMode": "awsvpc", 27 | "volumes": [], 28 | "status": "ACTIVE", 29 | "requiresAttributes": [ 30 | { 31 | "name": "com.amazonaws.ecs.capability.ecr-auth" 32 | }, 33 | { 34 | "name": "com.amazonaws.ecs.capability.docker-remote-api.1.21" 35 | }, 36 | { 37 | "name": "com.amazonaws.ecs.capability.task-iam-role" 38 | }, 39 | { 40 | "name": "ecs.capability.container-ordering" 41 | }, 42 | { 43 | "name": "ecs.capability.execution-role-ecr-pull" 44 | }, 45 | { 46 | "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" 47 | }, 48 | { 49 | "name": "ecs.capability.task-eni" 50 | }, 51 | { 52 | "name": "ecs.capability.gmsa-domainless" 53 | } 54 | ], 55 | "placementConstraints": [], 56 | "compatibilities": [ 57 | "EC2", 58 | "FARGATE" 59 | ], 60 | "requiresCompatibilities": [ 61 | "EC2", 62 | "FARGATE" 63 | ], 64 | "cpu": "1024", 65 | "memory": "2048", 66 | "runtimePlatform": { 67 | "cpuArchitecture": "X86_64", 68 | "operatingSystemFamily": "LINUX" 69 | }, 70 | "tags": [] 71 | } 72 | -------------------------------------------------------------------------------- /cdk/ECS_task_definition/README.txt: -------------------------------------------------------------------------------- 1 | Before running start_stack.sh: 2 | 1) Build Dockerfile and push to your ECR at AWS_ACCOUNT_NUMBER.dkr.ecr.us-west-1.amazonaws.com/my-mssql-tools:latest, make sure to replace AWS_ACCOUNT_NUMBER with your AWS account number. 3 | 2) Replace AWS_ACCOUNT_NUMBER and S3_CREDSPEC_LOCATION in CredentialsfetcherADStackCredentialsFetcherTaskDefinitiontemplate.json 4 | 3) Use CredentialsfetcherADStackCredentialsFetcherTaskDefinitiontemplate.json to create an ECS task definition EC2 console. 5 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | __pycache__ 4 | .pytest_cache 5 | *.egg-info 6 | 7 | # CDK asset staging directory 8 | .cdk.staging 9 | cdk.out 10 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/mssql-tools 2 | RUN cat /etc/os-release 3 | RUN apt-get update -y 4 | RUN apt-get install krb5-user unzip inetutils-ping dnsutils -y 5 | ENV KRB5CCNAME=/var/credentials-fetcher/krbdir/krb5cc 6 | CMD ["sleep", "infinity"] -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | CDK automation to run Linux gMSA in ECS with EC2 instance in non domain-joined mode. 3 | 4 | #### This CDK does the following: 5 | - Create directory in Directory Service (Active Directory) 6 | - Launch Windows instance, domain-join it with Active Directory and create gMSA accounts 7 | - Create ECS cluster 8 | - Launch ECS-optimized Linux instance and attach to ECS cluster 9 | - Run ECS tasks in the ECS-optimized Linux instances using gMSA in non domain-joined mode. 10 | 11 | ##### Disclaimer: This CDK and scripts are only for test, please modify as needed. 12 | 13 | ### Setup 14 | 15 | Create the following environment variables: 16 | 1. AWS_REGION 17 | 2. S3_PREFIX 18 | 3. KEY_PAIR_NAME 19 | 4. PREFIX_LIST 20 | 21 | - Please take a look at data.json for default values. 22 | - If you're testing a new RPM, upload it in the S3 bucket. 23 | - Ensure you have docker running in the background. 24 | 25 | 1. Update data.json, and make sure there are no values with "xxxxxxxx" 26 | 27 | 2. `default` AWS profile with administrator access is needed, a separate/burner AWS account would suffice. 28 | 29 | 3. Create a virtual environment 30 | ``` 31 | # Go to cdk directory 32 | 33 | $ cd cdk/ 34 | 35 | # To manually create a virtualenv on MacOS and Linux: 36 | 37 | $ python3 -m venv .venv 38 | 39 | # After the init process completes and the virtualenv is created, you can use the following step to activate your virtualenv. 40 | 41 | $ source .venv/bin/activate 42 | 43 | Once the virtualenv is activated, you can install the required dependencies. 44 | 45 | $ cd cdk-domainless-mode 46 | $ pip install -r requirements.txt 47 | 48 | Install AWS cdk 49 | 50 | $ brew install aws-cdk 51 | ``` 52 | 53 | 4. Run start_stack.sh (this is a bash script) to create a CloudFormation stack. 54 | 55 | 4.1 Update start_stack.sh with your aws account number 56 | 57 | 4.2 This creates Managed Active Directory, launches Windows instance and domain-joins it and creates the gMSA accounts, launches an ECS-optimized Linux instance, creates a new ECS cluster and attaches it to ECS cluster. 58 | ``` 59 | (.venv) cd tests/ 60 | (.venv) tests % ./start_stack.sh 61 | [10:29:46] CDK toolkit version: 2.156.0 (build 2966832) 62 | [10:29:46] Command line arguments: { 63 | _: [ 'bootstrap' ], 64 | ``` 65 | 66 | 5. Run End-To-End SQL test with Credentials Fetcher ECS Domainless Setup 67 | ``` 68 | (.venv) tests % python3 run_e2e_test.py 69 | ``` 70 | 6. Done! If everything worked as expected, you should see an output like this in the terminal: 71 | ``` 72 | EmpID EmpName Designation DepartmentJoiningDate 73 | ----------- -------------------------------------------------- -------------------------------------------------- ------------------------------------------------------------------------- 74 | 1 CHIN YEN LAB ASSISTANT LAB2022-03-05 03:57:09.967 75 | 2 MIKE PEARL SENIOR ACCOUNTANT ACCOUNTS2022-03-05 03:57:09.967 76 | 3 GREEN FIELD ACCOUNTANT ACCOUNTS2022-03-05 03:57:09.967 77 | 4 DEWANE PAUL PROGRAMMER IT2022-03-05 03:57:09.967 78 | 5 MATTS SR. PROGRAMMER IT2022-03-05 03:57:09.967 79 | 6 PLANK OTO ACCOUNTANT ACCOUNTS2022-03-05 03:57:09.967 80 | 81 | (6 rows affected) 82 | ``` 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | import aws_cdk as cdk 5 | 6 | from cdk.cdk_stack import CdkStack 7 | import aws_cdk.aws_ec2 as ec2 8 | import aws_cdk as cdk 9 | import aws_cdk.aws_secretsmanager as secretsmanager 10 | 11 | import json 12 | 13 | # Open the input file 14 | with open('data.json', 'r') as file: 15 | # Load the JSON data 16 | data = json.load(file) 17 | 18 | def get_value(key): 19 | return os.environ.get(key, data.get(key.lower())) 20 | 21 | tag = cdk.Tag("Name", "Test Credentials-fetcher in Domainless mode") 22 | aws_region = get_value("AWS_REGION") 23 | prefix_list = get_value("PREFIX_LIST") 24 | domain_admin_password = data["domain_admin_password"] 25 | directory_name = data["directory_name"] 26 | windows_instance_tag = data["windows_instance_tag"] 27 | linux_instance_tag = data["linux_instance_tag"] 28 | key_name = get_value("KEY_PAIR_NAME") 29 | number_of_gmsa_accounts = data["number_of_gmsa_accounts"] 30 | s3_bucket = get_value("S3_PREFIX") + data["s3_bucket_suffix"] 31 | app_name = data["stack_name"] 32 | username = data["username"] 33 | password = data["password"] 34 | secret_name = data["secret_name"] 35 | task_definition_template_name = data["task_definition_template_name"] 36 | cluster_name = data["cluster_name"] 37 | docker_image_tag = data["docker_image_tag"] 38 | dockerfile_path = data["dockerfile_path"] 39 | ecr_repo_name = data["ecr_repo_name"] 40 | rpm_file = data["rpm_file"] 41 | 42 | app = cdk.App() 43 | 44 | cdk_stack = CdkStack(app, app_name) 45 | 46 | cdk_stack.init_vpc(prefix_list = prefix_list, key_pair_name=key_name, stack_name=app_name) 47 | 48 | cfn_microsoft_AD = cdk_stack.init_DirectoryService(directory_name=directory_name, domain_admin_password=domain_admin_password) 49 | 50 | directory_id = cfn_microsoft_AD.ref 51 | 52 | cdk_stack.init_route53_endpoint(domain_name = directory_name, 53 | vpc = cdk_stack.vpc) 54 | 55 | windows_instance = cdk_stack.launch_windows_instance(instance_tag = windows_instance_tag, 56 | password = domain_admin_password, 57 | domain_name = directory_name, 58 | key_name = key_name, 59 | number_of_gmsa_accounts = number_of_gmsa_accounts, 60 | s3_bucket_name = s3_bucket 61 | ) 62 | 63 | windows_instance.node.add_dependency(cfn_microsoft_AD) 64 | 65 | ecs_cluster = cdk_stack.create_ecs_cluster( cluster_name, 66 | instance_tag=linux_instance_tag, 67 | password = domain_admin_password, 68 | domain_name = directory_name, 69 | key_pair=cdk_stack.key_pair, 70 | number_of_gmsa_accounts=number_of_gmsa_accounts, 71 | vpc = cdk_stack.vpc, 72 | security_group=cdk_stack.security_group, 73 | rpm_file=rpm_file, 74 | s3_bucket=s3_bucket) 75 | ecs_cluster.node.add_dependency(windows_instance) 76 | 77 | task_definition = cdk_stack.create_task_definition(task_definition_template_name=task_definition_template_name) 78 | 79 | docker_image_uri = cdk_stack.build_push_dockerfile_to_ecr(dockerfile_path, ecr_repo_name, aws_region, docker_image_tag) 80 | 81 | app.synth() 82 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "source.bat", 12 | "**/__init__.py", 13 | "**/__pycache__", 14 | "tests" 15 | ] 16 | }, 17 | "context": { 18 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 19 | "@aws-cdk/core:checkSecretUsage": true, 20 | "@aws-cdk/core:target-partitions": [ 21 | "aws", 22 | "aws-cn" 23 | ], 24 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 25 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 26 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 27 | "@aws-cdk/aws-iam:minimizePolicies": true, 28 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 29 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 30 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 31 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 32 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 33 | "@aws-cdk/core:enablePartitionLiterals": true, 34 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 35 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 36 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 37 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 38 | "@aws-cdk/aws-route53-patters:useCertificate": true, 39 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false, 40 | "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, 41 | "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, 42 | "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, 43 | "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, 44 | "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, 45 | "@aws-cdk/aws-redshift:columnId": true, 46 | "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, 47 | "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, 48 | "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, 49 | "@aws-cdk/aws-kms:aliasNameRef": true, 50 | "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, 51 | "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, 52 | "@aws-cdk/aws-efs:denyAnonymousAccess": true, 53 | "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, 54 | "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, 55 | "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, 56 | "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, 57 | "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, 58 | "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, 59 | "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, 60 | "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, 61 | "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, 62 | "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, 63 | "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, 64 | "@aws-cdk/aws-eks:nodegroupNameAttribute": true, 65 | "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, 66 | "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, 67 | "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, 68 | "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/cdk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/credentials-fetcher/dc5c2caec5e78052327b39cf2528eea7b2f45c91/cdk/cdk-domainless-mode/cdk/__init__.py -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/cleanup.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | 4 | # Open the input file 5 | with open('data.json', 'r') as file: 6 | # Load the JSON data 7 | data = json.load(file) 8 | 9 | directory_name = data["directory_name"] 10 | netbios_name = data["netbios_name"] 11 | number_of_gmsa_accounts = data["number_of_gmsa_accounts"] 12 | stack_name = data["stack_name"] 13 | cluster_name = data["cluster_name"] 14 | vpc_name = data["vpc_name"] 15 | task_definition_template_name = data["task_definition_template_name"] 16 | 17 | 18 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/credspec_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "CmsPlugins": ["ActiveDirectory"], 3 | "DomainJoinConfig": { 4 | "Sid": "S-1-5-21-2421564706-1737585382-3854682907", 5 | "MachineAccountName": "$gmsaName$", 6 | "Guid": "6a91814c-e151-4fb0-96f0-f517566fc883", 7 | "DnsTreeName": "$adDomainName$", 8 | "DnsName": "$adDomainName$", 9 | "NetBiosName": "$adNetBiosName$" 10 | }, 11 | "ActiveDirectoryConfig": { 12 | "GroupManagedServiceAccounts": [ 13 | { 14 | "Name": "$gmsaName$", 15 | "Scope": "$adDomainName$" 16 | }, 17 | { 18 | "Name": "$gmsaName$", 19 | "Scope": "$adNetBiosName$" 20 | } 21 | ], 22 | "HostAccountConfig": { 23 | "PortableCcgVersion": "1", 24 | "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}", 25 | "PluginInput": { 26 | "CredentialArn": "$gmsaSecretArn$" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "stack_name": "Credentials-fetcher-AD-Stack", 3 | "aws_region": "us-west-2", 4 | "prefix_list": "xxxxxxxx", 5 | "domain_admin_username": "admin", 6 | "domain_admin_password": "Qn:51eJsORJNL@~{HY@?", 7 | "key_pair_name": "xxxxxxxx", 8 | "directory_name": "contoso.com", 9 | "netbios_name" : "contoso", 10 | "windows_instance_tag": "ActiveDirectoryManagementInstance", 11 | "linux_instance_tag": "CredentialsFetcherLinuxInstance", 12 | "s3_prefix": "xxxxxxxx", 13 | "s3_bucket_suffix": "-credentials-fetcher-pre-created-bucket", 14 | "number_of_gmsa_accounts": 10, 15 | "aws_profile_name": "default", 16 | "username": "StandardUser01", 17 | "password": "p@ssw0rd", 18 | "secret_name": "aws/directoryservice/contoso/gmsa", 19 | "task_definition_template_name": "CredentialsFetcherTaskDefinitiontemplate", 20 | "cluster_name": "Credentials-fetcher-ecs-load-test", 21 | "vpc_name": "Credentials-fetcher-AD-Stack-vpc", 22 | "ecr_repo_name": "my-ecr-repo", 23 | "docker_image_tag": "latest", 24 | "dockerfile_path": "./Dockerfile", 25 | "rpm_file": "xxxxxxxx", 26 | "max_tasks_per_instance": 3 27 | } -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pytest==6.2.5 2 | boto3>=1.35.15 3 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk-lib==2.156.0 2 | constructs>=10.0.0,<11.0.0 3 | boto3>=1.35.15 4 | docker 5 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/source.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem The sole purpose of this script is to make the command 4 | rem 5 | rem source .venv/bin/activate 6 | rem 7 | rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows. 8 | rem On Windows, this command just runs this batch file (the argument is ignored). 9 | rem 10 | rem Now we don't need to document a Windows command for activating a virtualenv. 11 | 12 | echo Executing .venv\Scripts\activate.bat for you 13 | .venv\Scripts\activate.bat 14 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/start_stack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Install Docker if not present 4 | if ! command -v docker &> /dev/null; then 5 | echo "Please install and setup docker daemon and ensure it's running." 6 | exit 7 | fi 8 | 9 | echo "Please edit the file to add your AWS account number below" 10 | cdk bootstrap aws://XXXXXXXXXXXX/us-west-1 --trust=XXXXXXXXXXXX --cloudformation-execution-policies=arn:aws:iam::aws:policy/AdministratorAccess --verbose && cdk synth && cdk deploy 11 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/README.md: -------------------------------------------------------------------------------- 1 | ### Test Scripts 2 | 3 | #### Pre Requisites 4 | - Ensure cdk stack is deployed to your personal account 5 | - Create a new AL2023/Ubuntu instance in the ADStack VPC 6 | - Install credentials-fetcher dependencies using dnf 7 | ```aiignore 8 | dnf install -y realmd 9 | dnf install -y oddjob 10 | dnf install -y oddjob-mkhomedir 11 | dnf install -y sssd 12 | dnf install -y adcli 13 | dnf install -y krb5-workstation 14 | dnf install -y samba-common-tools 15 | ``` 16 | - Install the latest credentials-fetcher rpm in this instance 17 | - Run credentials-fetcher rpm as a systemd process 18 | ```aiignore 19 | systemctl start credentials-fetcher 20 | systemctl status credentials-fetcher 21 | ``` 22 | - Clone credentials-fetcher repo and create a python proto file 23 | ```aiignore 24 | git clone -b dev https://github.com/aws/credentials-fetcher.git 25 | cd credentials-fetcher/cdk/cdk-domainless-mode/test-scripts 26 | python3 -m venv .venv 27 | source .venv/bin/activate 28 | pip install grpcio-tools 29 | python3 -m grpc_tools.protoc -I/home/ec2-user/credentials-fetcher/protos --python_out=. --grpc_python_out=. credentialsfetcher.proto 30 | ``` -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/add_delete_kerberos_leases.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | import credentialsfetcher_pb2 3 | import credentialsfetcher_pb2_grpc 4 | import os 5 | import json 6 | import time 7 | 8 | ''' 9 | Use this script to create and delete N kerberos leases in a recurring loop 10 | (currently set to 100 times). This script is run to test that create/delete 11 | functionality has no leaks or unexpected failures when run over a long 12 | period of time. This script is run on a linux instance in stand-alone mode. 13 | ''' 14 | 15 | with open('../data.json', 'r') as file: 16 | # Load the JSON data 17 | data = json.load(file) 18 | 19 | def run(): 20 | with grpc.insecure_channel('unix:///var/credentials-fetcher/socket/credentials_fetcher.sock') as channel: 21 | stub = credentialsfetcher_pb2_grpc.CredentialsFetcherServiceStub(channel) 22 | number_of_gmsa_accounts = data["number_of_gmsa_accounts"] 23 | directory_name = data["directory_name"] 24 | netbios_name = data["netbios_name"] 25 | username = data["username"] 26 | password = data["password"] 27 | 28 | for iter in range(100): # Repeat the process 100 times 29 | lease_ids = [] 30 | 31 | # Create cred-specs for users ending with multiples of 5 32 | for i in range(2, number_of_gmsa_accounts, 2): 33 | credspec_contents = f"""{{ 34 | "CmsPlugins": ["ActiveDirectory"], 35 | "DomainJoinConfig": {{ 36 | "Sid": "S-1-5-21-2725122404-4129967127-2630707939", 37 | "MachineAccountName": "WebApp0{i}", 38 | "Guid": "e96e0e09-9305-462f-9e44-8a8179722897", 39 | "DnsTreeName": "{directory_name}", 40 | "DnsName": "{directory_name}", 41 | "NetBiosName": "{netbios_name}" 42 | }}, 43 | "ActiveDirectoryConfig": {{ 44 | "GroupManagedServiceAccounts": [ 45 | {{"Name": "WebApp0{i}", "Scope": "{directory_name}"}}, 46 | {{"Name": "WebApp0{i}", "Scope": "{netbios_name}"}} 47 | ], 48 | "HostAccountConfig": {{ 49 | "PortableCcgVersion": "1", 50 | "PluginGUID": "{{GDMA0342-266A-4D1P-831J-20990E82944F}}", 51 | "PluginInput": {{ 52 | "CredentialArn": "aws/directoryservice/contoso/gmsa" 53 | }} 54 | }} 55 | }} 56 | }}""" 57 | 58 | contents = [credspec_contents] 59 | response = stub.AddNonDomainJoinedKerberosLease( 60 | credentialsfetcher_pb2.CreateNonDomainJoinedKerberosLeaseRequest( 61 | credspec_contents=contents, 62 | username=username, 63 | password=password, 64 | domain=directory_name 65 | ) 66 | ) 67 | print(f"Created lease for WebApp0{i}: {response.lease_id}") 68 | lease_path = (f"/var/credentials-fetcher/krbdir/" 69 | f"{response.lease_id}/WebApp0{i}/krb5cc") 70 | assert os.path.exists(lease_path) 71 | lease_ids.append(response.lease_id) 72 | 73 | # Small delay to allow for processing 74 | time.sleep(1) 75 | 76 | # Delete the created cred-specs 77 | for lease_id in lease_ids: 78 | delete_response = stub.DeleteKerberosLease( 79 | credentialsfetcher_pb2.DeleteKerberosLeaseRequest( 80 | lease_id=lease_id 81 | ) 82 | ) 83 | print(f"Deleted lease: {delete_response.lease_id}") 84 | lease_path = f"/var/credentials-fetcher/krbdir/{lease_id}" 85 | print(lease_path) 86 | assert not os.path.exists(lease_path) 87 | 88 | print(f"Completed {iter} cycle of creation and deletion") 89 | 90 | if __name__ == '__main__': 91 | run() -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/create_domain_joined_AD_accounts.ps1: -------------------------------------------------------------------------------- 1 | # Use this script to create new Domain Joined gMSA accounts and add them to 2 | # the AD. This script is run on the Windows Instance with access to Managed AD. 3 | 4 | $username = "admin@CONTOSO.COM" 5 | $password = "Qn:51eJsORJNL@~{HY@?" | ConvertTo-SecureString -AsPlainText -Force 6 | $credential = New-Object System.Management.Automation.PSCredential($username, $password) 7 | 8 | $groupAllowedToRetrievePassword = "WebAppAccounts_OU" 9 | $path = "OU=MYOU,OU=Users,OU=contoso,DC=contoso,DC=com" 10 | 11 | for (($i = 1); $i -le 10;$i++) 12 | { 13 | # Create the gMSA account 14 | $gmsa_account_name = "DJ_WebApp0" + $i 15 | $gmsa_account_with_domain = $gmsa_account_name + "." + $env:USERDNSDOMAIN 16 | $gmsa_account_with_host = "host/" + $gmsa_account_name 17 | $gmsa_account_with_host_and_domain = $gmsa_account_with_host + "." + $env:USERDNSDOMAIN 18 | 19 | try { 20 | New-ADServiceAccount -Name $gmsa_account_name ` 21 | -DnsHostName $gmsa_account_with_domain ` 22 | -ServicePrincipalNames $gmsa_account_with_host, $gmsa_account_with_host_and_domain ` 23 | -PrincipalsAllowedToRetrieveManagedPassword $groupAllowedToRetrievePassword ` 24 | -Path $path ` 25 | -Credential $credential ` 26 | -Server $env:USERDNSDOMAIN ` 27 | -KerberosEncryptionType AES256 28 | Write-Output "Created gMSA account: $gmsa_account_name" 29 | } catch { 30 | $string_err = $_ | Out-String 31 | Write-Output "Error while gMSA account creation: " + $string_err 32 | } 33 | } -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/create_domain_joined_kerberos_leases.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | import credentialsfetcher_pb2 3 | import credentialsfetcher_pb2_grpc 4 | import json 5 | import os 6 | ''' 7 | Use this script to create and test N leases for N domain-joined gMSA 8 | accounts. This script is run on a linux instance in stand-alone mode. 9 | ''' 10 | with open('../data.json', 'r') as file: 11 | # Load the JSON data 12 | data = json.load(file) 13 | 14 | def run(): 15 | with grpc.insecure_channel('unix:///var/credentials-fetcher/socket/credentials_fetcher.sock') as channel: 16 | stub = credentialsfetcher_pb2_grpc.CredentialsFetcherServiceStub(channel) 17 | number_of_gmsa_accounts = data["number_of_gmsa_accounts"] 18 | directory_name = data["directory_name"] 19 | netbios_name = data["netbios_name"] 20 | for i in range(1, number_of_gmsa_accounts): 21 | credspec_contents = f"""{{ 22 | "CmsPlugins": ["ActiveDirectory"], 23 | "DomainJoinConfig": {{ 24 | "Sid": "S-1-5-21-2725122404-4129967127-2630707939", 25 | "MachineAccountName": "DJ_WebApp0{i}", 26 | "Guid": "e96e0e09-9305-462f-9e44-8a8179722897", 27 | "DnsTreeName": "{directory_name}", 28 | "DnsName": "{directory_name}", 29 | "NetBiosName": "{netbios_name}" 30 | }}, 31 | "ActiveDirectoryConfig": {{ 32 | "GroupManagedServiceAccounts": [ 33 | {{"Name": "DJ_WebApp0{i}", "Scope": "{directory_name}"}}, 34 | {{"Name": "DJ_WebApp0{i}", "Scope": "{netbios_name}"}} 35 | ] 36 | }} 37 | }}""" 38 | 39 | contents = [credspec_contents] 40 | response = stub.AddKerberosLease( 41 | credentialsfetcher_pb2.CreateKerberosLeaseRequest( 42 | credspec_contents=contents 43 | ) 44 | ) 45 | lease_path = (f"/var/credentials-fetcher/krbdir/" 46 | f"{response.lease_id}/DJ_WebApp0{i}/krb5cc") 47 | assert os.path.exists(lease_path) 48 | print(f"Server response: {response}") 49 | 50 | if __name__ == '__main__': 51 | run() -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/create_non_domain_joined_AD_accounts.ps1: -------------------------------------------------------------------------------- 1 | # Use this script to create new Non Domain Joined gMSA accounts and add them to 2 | # the AD. This script is run on the Windows Instance with access to Managed AD. 3 | # NOTE: The cdk stack already creates N gmsa accounts where N corresponds to the number_of_gmsa_accounts in data 4 | # .json. Use this script if you would like to create new accounts without deploying/re-deploying the cdk stack 5 | 6 | $username = "admin@CONTOSO.COM" 7 | $password = "Qn:51eJsORJNL@~{HY@?" | ConvertTo-SecureString -AsPlainText -Force 8 | $credential = New-Object System.Management.Automation.PSCredential($username, $password) 9 | 10 | $groupAllowedToRetrievePassword = "WebAppAccounts_OU" 11 | $path = "OU=MYOU,OU=Users,OU=contoso,DC=contoso,DC=com" 12 | 13 | for (($i = 11); $i -le 200; $i++) 14 | { 15 | # Create the gMSA account 16 | $gmsa_account_name = "WebApp0" + $i 17 | $gmsa_account_with_domain = $gmsa_account_name + ".contoso.com" 18 | $gmsa_account_with_host = "host/" + $gmsa_account_name 19 | $gmsa_account_with_host_and_domain = $gmsa_account_with_host + ".contoso.com" 20 | 21 | try { 22 | #New-ADServiceAccount -Name serviceuser1 -Path "OU=MYOU1,OU=Users,OU=ActiveDirectory,DC=contoso,DC=com" -Credential $credential -DNSHostname "contoso.com" 23 | New-ADServiceAccount -Name $gmsa_account_name -DnsHostName $gmsa_account_with_domain -ServicePrincipalNames $gmsa_account_with_host, $gmsa_account_with_host_and_domain -PrincipalsAllowedToRetrieveManagedPassword $groupAllowedToRetrievePassword -Path $path -Credential $credential -Server contoso.com 24 | Write-Output "New-ADServiceAccount -Name $gmsa_account_name -DnsHostName $gmsa_account_with_domain -ServicePrincipalNames $gmsa_account_with_host, $gmsa_account_with_host_and_domain -PrincipalsAllowedToRetrieveManagedPassword $groupAllowedToRetrievePassword -Path $path -Credential $credential -Server contoso.com" 25 | } catch { 26 | $string_err = $_ | Out-String 27 | Write-Output "Error while gMSA account creation and copy credspec to S3 bucket: " + $string_err 28 | } 29 | } -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/create_non_domain_joined_kerberos_leases.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | import credentialsfetcher_pb2 3 | import credentialsfetcher_pb2_grpc 4 | import json 5 | import os 6 | 7 | ''' 8 | Use this script to create and test N leases for N non domain-joined gMSA 9 | accounts. This script is run on a linux instance in stand-alone mode. 10 | ''' 11 | 12 | with open('../data.json', 'r') as file: 13 | # Load the JSON data 14 | data = json.load(file) 15 | 16 | def run(): 17 | with grpc.insecure_channel('unix:///var/credentials-fetcher/socket/credentials_fetcher.sock') as channel: 18 | number_of_gmsa_accounts = data["number_of_gmsa_accounts"] 19 | directory_name = data["directory_name"] 20 | netbios_name = data["netbios_name"] 21 | username = data["username"] 22 | password = data["password"] 23 | stub = credentialsfetcher_pb2_grpc.CredentialsFetcherServiceStub(channel) 24 | for i in range(1, number_of_gmsa_accounts): 25 | credspec_contents = f"""{{ 26 | "CmsPlugins": ["ActiveDirectory"], 27 | "DomainJoinConfig": {{ 28 | "Sid": "S-1-5-21-2725122404-4129967127-2630707939", 29 | "MachineAccountName": "WebApp0{i}", 30 | "Guid": "e96e0e09-9305-462f-9e44-8a8179722897", 31 | "DnsTreeName": "{directory_name}", 32 | "DnsName": "{directory_name}", 33 | "NetBiosName": "{netbios_name}" 34 | }}, 35 | "ActiveDirectoryConfig": {{ 36 | "GroupManagedServiceAccounts": [ 37 | {{"Name": "WebApp0{i}", "Scope": "{directory_name}"}}, 38 | {{"Name": "WebApp0{i}", "Scope": "{netbios_name}"}} 39 | ], 40 | "HostAccountConfig": {{ 41 | "PortableCcgVersion": "1", 42 | "PluginGUID": "{{GDMA0342-266A-4D1P-831J-20990E82944F}}", 43 | "PluginInput": {{ 44 | "CredentialArn": "aws/directoryservice/contoso/gmsa" 45 | }} 46 | }} 47 | }} 48 | }}""" 49 | contents = [credspec_contents] 50 | response = stub.AddNonDomainJoinedKerberosLease( 51 | credentialsfetcher_pb2.CreateNonDomainJoinedKerberosLeaseRequest( 52 | credspec_contents=contents, 53 | username=username, 54 | password=password, 55 | domain=directory_name 56 | ) 57 | ) 58 | lease_path = (f"/var/credentials-fetcher/krbdir/" 59 | f"{response.lease_id}/WebApp0{i}/krb5cc") 60 | assert os.path.exists(lease_path) 61 | print(f"Server response: {response}") 62 | 63 | if __name__ == '__main__': 64 | run() -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/stress-test/README.md: -------------------------------------------------------------------------------- 1 | ## Stress Test 2 | 3 | This credentials-fetcher stress test validates the shutdown path including gRPC shutdown using repeated add and delete grpc APIs and daemon restarts in a loop. This test checks robustness of the credentials-fetcher daemon, ensuring that it can withstand network interruptions, broken connections, or system reboots without leaving behind unnecessary objects and always cleaning up after itself. 4 | 5 | #### Steps to run this test 6 | 7 | 1. Follow the setup instructions [here](https://github.com/aws/credentials-fetcher/blob/mainline/cdk/cdk-domainless-mode/test-scripts/README.md). 8 | 2. Create a tmux session `tmux new -s client1`. Run the client with `bash client.sh` 9 | 3. Repeat Step 2 as many times as needed (ex: 3 clients for a t3.medium instance). This creates unique clients that call the credentials-fetcher daemon. 10 | 4. To ensure robustness from the server side, create another tmux session `tmux new -s server`, and run the server-restart script as `bash server-restart.sh` This script restarts the daemon every 30 seconds while the client is still calling it. 11 | 5. Run `journalctl | grep -i assert` to check that there are no failed ASSERTs. 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/stress-test/client.sh: -------------------------------------------------------------------------------- 1 | while true; 2 | do 3 | python3 ../add_delete_kerberos_leases.py 4 | echo "Script exited, restarting in 2 seconds..." 5 | sleep 2 6 | done -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/test-scripts/stress-test/server-restart.sh: -------------------------------------------------------------------------------- 1 | while : 2 | do 3 | systemctl stop credentials-fetcher.service 4 | sleep 2 5 | systemctl start credentials-fetcher.service 6 | sleep 30 7 | echo "Restarted credentials fetcher after 30 seconds..." 8 | done -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/copy_credspecs_and_create_task_defs.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import os 4 | 5 | def setup_aws_session(aws_profile_name): 6 | boto3.setup_default_session(profile_name=aws_profile_name) 7 | 8 | def get_ecs_task_execution_role_arn(): 9 | iam_client = boto3.client('iam') 10 | list_roles = iam_client.list_roles(MaxItems=1000) 11 | for role in list_roles['Roles']: 12 | if 'CredentialsFetcher-ECSTaskExecutionRolegMSA' == role['RoleName']: 13 | return role['Arn'] 14 | return None 15 | 16 | def get_task_definition_template(ecs_client, task_definition_template_name): 17 | response = ecs_client.list_task_definitions() 18 | if 'taskDefinitionArns' in response: 19 | for arn in response['taskDefinitionArns']: 20 | if task_definition_template_name in arn: 21 | return ecs_client.describe_task_definition(taskDefinition=arn) 22 | print(f"No task definitions found matching '{task_definition_template_name}'") 23 | return None 24 | 25 | def get_ecs_cluster_info(ecs_client, ecs_cluster_name): 26 | ecs_clusters = ecs_client.list_clusters() 27 | for cluster_arn in ecs_clusters['clusterArns']: 28 | cluster_name = cluster_arn.split('/')[1] 29 | if cluster_name == ecs_cluster_name: 30 | ecs_cluster_instance_arn = ecs_client.list_container_instances(cluster=cluster_arn)['containerInstanceArns'][0] 31 | return cluster_arn, ecs_cluster_instance_arn 32 | return None, None 33 | 34 | def create_credspec(directory_name, netbios_name, gmsa_name, gmsa_secret_arn): 35 | credspec_template = """ 36 | { 37 | "CmsPlugins": ["ActiveDirectory"], 38 | "DomainJoinConfig": { 39 | "Sid": "S-1-5-21-2421564706-1737585382-3854682907", 40 | "MachineAccountName": "GMSA_NAME", 41 | "Guid": "6a91814c-e151-4fb0-96f0-f517566fc883", 42 | "DnsTreeName": "DOMAINNAME", 43 | "DnsName": "DOMAINNAME", 44 | "NetBiosName": "NETBIOS_NAME" 45 | }, 46 | "ActiveDirectoryConfig": { 47 | "GroupManagedServiceAccounts": [ 48 | { 49 | "Name": "GMSA_NAME", 50 | "Scope": "DOMAINNAME" 51 | }, 52 | { 53 | "Name": "GMSA_NAME", 54 | "Scope": "NETBIOS_NAME" 55 | } 56 | ], 57 | "HostAccountConfig": { 58 | "PortableCcgVersion": "1", 59 | "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}", 60 | "PluginInput": { 61 | "CredentialArn": "GMSA_SECRET_ARN" 62 | } 63 | } 64 | } 65 | } 66 | """ 67 | credspec_template = credspec_template.replace("DOMAINNAME", directory_name) 68 | credspec_template = credspec_template.replace("NETBIOS_NAME", netbios_name) 69 | credspec_template = credspec_template.replace("GMSA_NAME", gmsa_name) 70 | credspec_template = credspec_template.replace("GMSA_SECRET_ARN", gmsa_secret_arn) 71 | return json.loads(credspec_template) 72 | 73 | def upload_credspec_to_s3(s3_client, s3_bucket, gmsa_name, credspec): 74 | s3_key = f"{gmsa_name}_credspec.json" 75 | try: 76 | s3_client.put_object(Body=json.dumps(credspec), Bucket=s3_bucket, Key=s3_key) 77 | bucket_location = s3_client.get_bucket_location(Bucket=s3_bucket) 78 | bucket_arn = f"arn:aws:s3:::{s3_bucket}" 79 | return bucket_arn, s3_key 80 | except Exception as e: 81 | print(f"Error uploading credspec to S3: {e}") 82 | return None, None 83 | 84 | def modify_task_definition(task_definition, ecs_cluster_arn, bucket_arn, s3_key): 85 | task_definition = task_definition["taskDefinition"] 86 | task_definition["compatibilities"].append("FARGATE") 87 | 88 | for container_def in task_definition['containerDefinitions']: 89 | container_def['credentialSpecs']=[] 90 | credspec = container_def['credentialSpecs'] 91 | credspec = [d for d in credspec if 'credentialspecdomainless' not in d] 92 | credspec.append(f"credentialspecdomainless:{bucket_arn}/{s3_key}") 93 | container_def['credentialSpecs'] = credspec 94 | 95 | attributes = task_definition['requiresAttributes'] 96 | attributes.append({ 97 | "name": "ecs.capability.gmsa-domainless", 98 | "targetId": ecs_cluster_arn 99 | }) 100 | 101 | return task_definition 102 | 103 | def register_new_task_definition(ecs_client, task_definition, family, ecs_task_execution_role_arn): 104 | return ecs_client.register_task_definition( 105 | family=family, 106 | taskRoleArn=ecs_task_execution_role_arn, 107 | executionRoleArn=ecs_task_execution_role_arn, 108 | networkMode=task_definition['networkMode'], 109 | containerDefinitions=task_definition['containerDefinitions'], 110 | requiresCompatibilities=["EC2", "FARGATE"], 111 | runtimePlatform={'cpuArchitecture': 'X86_64', 'operatingSystemFamily': 'LINUX'}, 112 | cpu=task_definition['cpu'], 113 | memory=task_definition['memory'] 114 | ) 115 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/create_secrets.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | from parse_data_from_json import (number_of_gmsa_accounts, netbios_name, 4 | username, password, directory_name) 5 | 6 | def create_secrets(): 7 | # Initialize the AWS Secrets Manager client 8 | client = boto3.client('secretsmanager') 9 | 10 | # Base path for the secrets 11 | secret_name = "aws/directoryservice/contoso/gmsa" 12 | 13 | # Create the secret value 14 | secret_value = { 15 | "username": username, 16 | "password": password, 17 | "domainName": directory_name, 18 | # "distinguishedName": f"CN=WebApp0{i},OU=MYOU,OU=Users,OU={netbios_name},DC={netbios_name},DC=com" 19 | } 20 | 21 | try: 22 | # Create the secret 23 | response = client.create_secret( 24 | Name=secret_name, 25 | Description=f"Secret for WebApp01", 26 | SecretString=json.dumps(secret_value) 27 | ) 28 | print(f"Created secret: {secret_name}") 29 | 30 | except client.exceptions.ResourceExistsException: 31 | print(f"Secret already exists: {secret_name}") 32 | except Exception as e: 33 | print(f"Error creating secret {secret_name}: {str(e)}") 34 | return False 35 | return True -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/gmsa.ps1: -------------------------------------------------------------------------------- 1 | 2 | # This script does the following: 3 | # 1) Install/Update SSM agent - without this the domain-join can fail 4 | # 2) Create a new OU 5 | # 3) Create a new security group 6 | # 4) Create a new standard user account, this account's username and password needs to be stored in a secret store like AWS secrets manager. 7 | # 5) Add members to the security group that is allowed to retrieve gMSA password 8 | # 6) Create gMSA accounts with PrincipalsAllowedToRetrievePassword set to the security group created in 4) 9 | 10 | # Create a temporary directory for downloads 11 | $tempDir = "C:\temp" 12 | if (-not (Test-Path $tempDir)) { 13 | New-Item -ItemType Directory -Path $tempDir 14 | } 15 | 16 | # 1) Install SSM agent 17 | Write-Output "Updating SSM agent..." 18 | [System.Net.ServicePointManager]::SecurityProtocol = 'TLS12' 19 | $progressPreference = 'silentlyContinue' 20 | Invoke-WebRequest https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/windows_amd64/AmazonSSMAgentSetup.exe -OutFile $env:USERPROFILE\Desktop\SSMAgent_latest.exe 21 | Start-Process -FilePath $env:USERPROFILE\Desktop\SSMAgent_latest.exe -ArgumentList "/S" 22 | 23 | # To install the AD module on Windows Server, run Install-WindowsFeature RSAT-AD-PowerShell 24 | # To install the AD module on Windows 10 version 1809 or later, run Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0' 25 | # To install the AD module on older versions of Windows 10, see https://aka.ms/rsat 26 | Write-Output "Installing Active Directory management tools..." 27 | Install-WindowsFeature -Name "RSAT-AD-Tools" -IncludeAllSubFeature 28 | Install-WindowsFeature RSAT-AD-PowerShell 29 | Install-Module CredentialSpec 30 | Install-Module -Name SqlServer -AllowClobber -Force 31 | 32 | $username = "admin@DOMAINNAME" 33 | $password = "INPUTPASSWORD" | ConvertTo-SecureString -AsPlainText -Force 34 | $credential = New-Object System.Management.Automation.PSCredential($username, $password) 35 | $groupAllowedToRetrievePassword = "WebAppAccounts_OU" 36 | # This is the basedn path that needs to be in secrets manager as "distinguishedName" : "OU=MYOU,OU=Users,OU=ActiveDirectory,DC=contoso,DC=com" 37 | $path = "OU=MYOU,OU=Users,OU=contoso,DC=NETBIOS_NAME,DC=com" 38 | $supath = "OU=Users,OU=contoso,DC=contoso,DC=com" 39 | 40 | 41 | # 2) Create OU 42 | New-ADOrganizationalUnit -Name "MYOU" -Path "OU=Users,OU=contoso,DC=NETBIOS_NAME,DC=com" -Credential $credential 43 | 44 | # 3) Create the security group 45 | try { 46 | New-ADGroup -Name "WebApp Authorized Accounts in OU" -SamAccountName $groupAllowedToRetrievePassword -Credential $credential -GroupScope DomainLocal -Server DOMAINNAME 47 | } catch { 48 | Write-Output "Security Group created" 49 | } 50 | 51 | # 4) Create a new standard user account, this account's username and password needs to be stored in a secret store like AWS secrets manager. 52 | try { 53 | New-ADUser -Name "StandardUser01" -AccountPassword (ConvertTo-SecureString -AsPlainText "p@ssw0rd" -Force) -Enabled 1 -Credential $credential -Path $supath -Server DOMAINNAME 54 | } catch { 55 | Write-Output "Created StandardUser01" 56 | } 57 | 58 | # 5) Add members to the security group that is allowed to retrieve gMSA password 59 | try { 60 | Add-ADGroupMember -Identity $groupAllowedToRetrievePassword -Members "StandardUser01" -Credential $credential -Server DOMAINNAME 61 | Add-ADGroupMember -Identity $groupAllowedToRetrievePassword -Members "admin" -Credential $credential -Server DOMAINNAME 62 | } catch { 63 | Write-Output "Created AD Group $groupAllowedToRetrievePassword" 64 | } 65 | 66 | # 6) Create gMSA accounts with PrincipalsAllowedToRetrievePassword set to the security group created in 4) 67 | $string_err = "" 68 | for (($i = 1); $i -le NUMBER_OF_GMSA_ACCOUNTS; $i++) 69 | { 70 | # Create the gMSA account 71 | $gmsa_account_name = "WebApp0" + $i 72 | $gmsa_account_with_domain = $gmsa_account_name + ".DOMAINNAME" 73 | $gmsa_account_with_host = "host/" + $gmsa_account_name 74 | $gmsa_account_with_host_and_domain = $gmsa_account_with_host + ".DOMAINNAME" 75 | 76 | try { 77 | # Check if the service account already exists 78 | if (-not (Get-ADServiceAccount -Filter {Name -eq $gmsa_account_name} -ErrorAction SilentlyContinue)) { 79 | New-ADServiceAccount -Name $gmsa_account_name ` 80 | -DnsHostName $gmsa_account_with_domain ` 81 | -ServicePrincipalNames $gmsa_account_with_host, $gmsa_account_with_host_and_domain ` 82 | -PrincipalsAllowedToRetrieveManagedPassword $groupAllowedToRetrievePassword ` 83 | -Path $path ` 84 | -Credential $credential ` 85 | -Server DOMAINNAME 86 | Write-Output "Created new gMSA account: $gmsa_account_name" 87 | } else { 88 | Write-Output "gMSA account $gmsa_account_name already exists - skipping creation" 89 | } 90 | } catch { 91 | $string_err = $_ | Out-String 92 | Write-Output "Error while processing gMSA account $gmsa_account_name : $string_err" 93 | } 94 | } 95 | 96 | # Set the SQL Server instance name 97 | $sqlInstance = $env:computername 98 | 99 | New-NetFirewallRule -DisplayName "SQLServer default instance" -Direction Inbound -LocalPort 1433 -Protocol TCP -Action Allow 100 | New-NetFirewallRule -DisplayName "SQLServer Browser service" -Direction Inbound -LocalPort 1434 -Protocol UDP -Action Allow 101 | netsh advfirewall firewall add rule name = SQLPort dir = in protocol = tcp action = allow localport = 1433 remoteip = localsubnet profile = DOMAIN 102 | New-NetFirewallRule -DisplayName “AllowRDP” -Direction Inbound -Protocol TCP –LocalPort 3389 -Action Allow 103 | New-NetFirewallRule -DisplayName "AllowSQLServer" -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow 104 | 105 | 106 | # Create a connection string 107 | $connectionString0 = "Server=$sqlInstance;Integrated Security=True;" 108 | $connectionString1 = "Server=$sqlInstance;Database=EmployeesDB;Integrated Security=True;" 109 | 110 | $createDatabaseQuery = "CREATE DATABASE EmployeesDB" 111 | 112 | $query = @" 113 | CREATE TABLE dbo.EmployeesTable ( 114 | EmpID INT IDENTITY(1,1) PRIMARY KEY, 115 | EmpName VARCHAR(50) NOT NULL, 116 | Designation VARCHAR(50) NOT NULL, 117 | Department VARCHAR(50) NOT NULL, 118 | JoiningDate DATETIME NOT NULL 119 | ); 120 | 121 | INSERT INTO EmployeesDB.dbo.EmployeesTable (EmpName, Designation, Department, JoiningDate) 122 | VALUES 123 | ('CHIN YEN', 'LAB ASSISTANT', 'LAB', '2022-03-05 03:57:09.967'), 124 | ('MIKE PEARL', 'SENIOR ACCOUNTANT', 'ACCOUNTS', '2022-03-05 03:57:09.967'), 125 | ('GREEN FIELD', 'ACCOUNTANT', 'ACCOUNTS', '2022-03-05 03:57:09.967'), 126 | ('DEWANE PAUL', 'PROGRAMMER', 'IT', '2022-03-05 03:57:09.967'), 127 | ('MATTS', 'SR. PROGRAMMER', 'IT', '2022-03-05 03:57:09.967'), 128 | ('PLANK OTO', 'ACCOUNTANT', 'ACCOUNTS', '2022-03-05 03:57:09.967'); 129 | "@ 130 | 131 | Invoke-Sqlcmd -ConnectionString $connectionString0 -Query $createDatabaseQuery -QueryTimeout 60 132 | Invoke-Sqlcmd -ConnectionString $connectionString1 -Query $query 133 | 134 | # Sleep for 10 seconds 135 | Start-Sleep -Seconds 10 136 | 137 | # Loop through WebApp01$ to WebApp010$ 138 | for ($i = 1; $i -le NUMBER_OF_GMSA_ACCOUNTS; $i++) { 139 | $webAppName = "WebApp0$i`$" 140 | 141 | $createLoginQuery = @" 142 | CREATE LOGIN [NETBIOS_NAME\$webAppName] FROM WINDOWS WITH DEFAULT_DATABASE = [master], DEFAULT_LANGUAGE = [us_english]; 143 | USE [EmployeesDB]; 144 | CREATE USER [$webAppName] FOR LOGIN [NETBIOS_NAME\$webAppName]; 145 | ALTER ROLE [db_owner] ADD MEMBER [$webAppName]; 146 | ALTER AUTHORIZATION ON DATABASE::[EmployeesDB] TO [$webAppName]; 147 | "@ 148 | 149 | Write-Host "Creating login and granting permissions for $webAppName" 150 | Invoke-Sqlcmd -ConnectionString $connectionString0 -Query $createLoginQuery 151 | } 152 | 153 | 154 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/parse_data_from_json.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | def load_data(): 5 | with open('../data.json', 'r') as file: 6 | return json.load(file) 7 | 8 | def get_value(key): 9 | return os.environ.get(key, data.get(key.lower())) 10 | 11 | data = load_data() 12 | 13 | number_of_gmsa_accounts = data["number_of_gmsa_accounts"] 14 | netbios_name = data["netbios_name"] 15 | directory_name = data["directory_name"] 16 | instance_name = data["windows_instance_tag"] 17 | region = data["aws_region"] 18 | stack_name = data["stack_name"] 19 | cluster_name = data["cluster_name"] 20 | vpc_name = data["vpc_name"] 21 | task_definition_template_name = data["task_definition_template_name"] 22 | repository_name = data["ecr_repo_name"] 23 | tag = data["docker_image_tag"] 24 | bucket_name = get_value("S3_PREFIX") + data["s3_bucket_suffix"] 25 | aws_profile_name = data["aws_profile_name"] 26 | username = data["username"] 27 | password = data["password"] 28 | windows_instance_tag = data["windows_instance_tag"] 29 | domain_admin_password = data["domain_admin_password"] 30 | containers_per_instance = data["max_tasks_per_instance"] * 10 # maximum number of container definitions per task is 10 (ref: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-quotas.html) 31 | 32 | if "XXX" in bucket_name: 33 | print("S3_PREFIX is not setup correctly, please set it and retry") 34 | exit(1) -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/run_sql_test.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import sys 3 | from parse_data_from_json import stack_name, windows_instance_tag, region 4 | 5 | """ 6 | This script executes a shell script on the Linux instance via SSM that: 7 | a. Lists Docker containers 8 | b. Identifies a specific Docker container (my-ecr-repo:latest) 9 | c. If the container is found, runs 'klist' and a SQL query inside the container 10 | 11 | This script validates that the Kerberos ticket can be used to access SQL server. 12 | 13 | """ 14 | 15 | def run_shell_script(instance_id, hostname): 16 | 17 | commands = [ 18 | 'systemctl restart credentials-fetcher', 19 | 'systemctl restart ecs', 20 | f'HOSTNAME="{hostname}"', 21 | 'echo "Listing all Docker containers:"', 22 | 'IMAGEID=$(docker ps --format "{{.ID}} {{.Image}}" | grep "my-ecr-repo:latest" | awk \'{print $1}\' | head -n 1)', 23 | 'echo "IMAGEID: $IMAGEID"', 24 | 'if [ -n "$IMAGEID" ]; then', 25 | ' echo "Container ID: $IMAGEID"', 26 | ' echo "Running commands inside the container:"', 27 | ' echo "klist && sqlcmd -S $HOSTNAME.contoso.com -C -Q \'SELECT * FROM employeesdb.dbo.employeestable;\'" | docker exec -i $IMAGEID env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/mssql-tools/bin bash', 28 | ' SQL_EXIT_CODE=$?', 29 | ' echo "SQL_SUCCESS_MARKER: $SQL_EXIT_CODE"', 30 | 'else', 31 | ' echo "No container found with my-ecr-repo:latest"', 32 | 'fi' 33 | ] 34 | ssm = boto3.client('ssm') 35 | 36 | response = ssm.send_command( 37 | InstanceIds=[instance_id], 38 | DocumentName="AWS-RunShellScript", 39 | Parameters={'commands': commands} 40 | ) 41 | 42 | command_id = response['Command']['CommandId'] 43 | 44 | waiter = ssm.get_waiter('command_executed') 45 | try: 46 | waiter.wait( 47 | CommandId=command_id, 48 | InstanceId=instance_id, 49 | WaiterConfig={ 50 | 'Delay': 30, 51 | 'MaxAttempts': 100 52 | } 53 | ) 54 | except Exception as e: 55 | print(f"Command failed: {commands}") 56 | print(f"Error: {str(e)}") 57 | raise 58 | 59 | output = ssm.get_command_invocation( 60 | CommandId=command_id, 61 | InstanceId=instance_id 62 | ) 63 | 64 | print(f"Command output:\n{output.get('StandardOutputContent', '')}") 65 | 66 | sql_success = False 67 | 68 | if output['Status'] == 'Success': 69 | print(f"Command status: Success") 70 | # Look for the SQL success marker in the output 71 | output_content = output.get('StandardOutputContent', '') 72 | for line in output_content.splitlines(): 73 | if line.startswith('SQL_SUCCESS_MARKER: '): 74 | exit_code = int(line.split(': ')[1]) 75 | if exit_code == 0: 76 | sql_success = True 77 | break 78 | else: 79 | print(f"Command failed with status: {output['Status']}") 80 | print(f"Error: {output.get('StandardErrorContent', 'No error content available')}") 81 | raise Exception(f"Command execution failed with status: {output['Status']}") 82 | 83 | return sql_success 84 | 85 | def get_windows_hostname(instance_id): 86 | commands = [ 87 | 'hostname' 88 | ] 89 | ssm = boto3.client('ssm') 90 | 91 | response = ssm.send_command( 92 | InstanceIds=[instance_id], 93 | DocumentName="AWS-RunPowerShellScript", 94 | Parameters={'commands': commands} 95 | ) 96 | 97 | command_id = response['Command']['CommandId'] 98 | 99 | waiter = ssm.get_waiter('command_executed') 100 | try: 101 | waiter.wait( 102 | CommandId=command_id, 103 | InstanceId=instance_id, 104 | WaiterConfig={ 105 | 'Delay': 30, 106 | 'MaxAttempts': 50 107 | } 108 | ) 109 | except Exception as e: 110 | print(f"Command failed: {commands}") 111 | print(f"Error: {str(e)}") 112 | raise 113 | 114 | output = ssm.get_command_invocation( 115 | CommandId=command_id, 116 | InstanceId=instance_id 117 | ) 118 | 119 | print(f"Command output:\n{output.get('StandardOutputContent', '')}") 120 | 121 | if output['Status'] == 'Success': 122 | hostname = output['StandardOutputContent'].strip() 123 | return hostname 124 | else: 125 | print(f"Command failed with status: {output['Status']}") 126 | print(f"Error: {output.get('StandardErrorContent', 'No error content available')}") 127 | raise Exception(f"Command execution failed with status: {output['Status']}") 128 | 129 | def get_instance_id_by_name(region, instance_name): 130 | 131 | ec2 = boto3.client('ec2', region_name=region) 132 | response = ec2.describe_instances( 133 | Filters=[ 134 | { 135 | 'Name': 'tag:Name', 136 | 'Values': [instance_name] 137 | }, 138 | { 139 | 'Name': 'instance-state-name', 140 | 'Values': ['running'] 141 | } 142 | ] 143 | ) 144 | 145 | for reservation in response['Reservations']: 146 | for instance in reservation['Instances']: 147 | return instance['InstanceId'] 148 | 149 | return None 150 | 151 | 152 | # instance_name_linux = stack_name + '/MyAutoScalingGroup' 153 | # instance_name_windows = windows_instance_tag 154 | # instance_id_linux = get_instance_id_by_name(region, instance_name_linux) 155 | # instance_id_windows = get_instance_id_by_name(region, instance_name_windows) 156 | # 157 | # hostname = get_windows_hostname(instance_id_windows) 158 | # run_shell_script(instance_id_linux, hostname) 159 | 160 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/setup_windows_instance.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | from parse_data_from_json import (domain_admin_password, directory_name, 5 | netbios_name, number_of_gmsa_accounts, 6 | bucket_name, region, instance_name) 7 | 8 | """ 9 | This script sets up the EC2 Windows instance. 10 | 11 | This script performs the following operations: 12 | 13 | 1. Retrieves an EC2 instance ID based on a tag name. 14 | 2. Reads a PowerShell script ('gmsa.ps1') from the directory. 15 | 3. Executes the PowerShell script on the specified EC2 instance using AWS Systems Manager (SSM). 16 | 4. The Powershell script does the following: 17 | - Installs AD management tools and related PowerShell modules. 18 | - Creates a new Organizational Unit (OU) in Active Directory. 19 | - Creates a new security group for gMSA account management. 20 | - Creates a new standard user account. 21 | - Adds members to the security group for gMSA password retrieval. 22 | - Creates multiple Group Managed Service Accounts (gMSA) based on a specified count. 23 | - Configures SQL Server firewall rules: 24 | - Allows inbound traffic on ports 1433 (TCP) and 1434 (UDP). 25 | - Sets up rules for RDP and SQL Server access. 26 | - Creates a new SQL Server database named 'EmployeesDB'. 27 | - Creates a table 'EmployeesTable' and inserts sample data. 28 | - Alters the database authorization to allow access from a gMSA account. 29 | 30 | It's designed to automate the process of running a PowerShell script on a Windows EC2 instance, 31 | for configuring Group Managed Service Accounts (gMSA) and related AWS resources. 32 | 33 | """ 34 | 35 | def run_powershell_script(instance_id, script_path): 36 | try: 37 | with open(script_path, 'r') as file: 38 | script_content = file.read() 39 | 40 | script_content = script_content.replace("INPUTPASSWORD", domain_admin_password) 41 | script_content = script_content.replace("DOMAINNAME", directory_name) 42 | script_content = script_content.replace("NETBIOS_NAME", netbios_name) 43 | script_content = script_content.replace("NUMBER_OF_GMSA_ACCOUNTS", str(number_of_gmsa_accounts)) 44 | script_content = script_content.replace("BUCKET_NAME", bucket_name) 45 | 46 | ssm = boto3.client('ssm') 47 | 48 | response = ssm.send_command( 49 | InstanceIds=[instance_id], 50 | DocumentName="AWS-RunPowerShellScript", 51 | Parameters={'commands': [script_content]} 52 | ) 53 | 54 | command_id = response['Command']['CommandId'] 55 | 56 | waiter = ssm.get_waiter('command_executed') 57 | waiter.wait( 58 | CommandId=command_id, 59 | InstanceId=instance_id, 60 | WaiterConfig={ 61 | 'Delay': 30, 62 | 'MaxAttempts': 50 63 | } 64 | ) 65 | 66 | output = ssm.get_command_invocation( 67 | CommandId=command_id, 68 | InstanceId=instance_id 69 | ) 70 | 71 | print(f"Command output:\n{output.get('StandardOutputContent', '')}") 72 | 73 | if output['Status'] == 'Success': 74 | print(f"Command status: Success") 75 | return True 76 | else: 77 | print(f"Command failed: {script_content}") 78 | print(f"Error: {output['StandardErrorContent']}") 79 | return False 80 | 81 | except Exception as e: 82 | print(f"An error occurred: {str(e)}") 83 | print(f"Command failed: {script_content}") 84 | return False 85 | 86 | def get_instance_id_by_name(region, instance_name): 87 | 88 | ec2 = boto3.client('ec2', region_name=region) 89 | response = ec2.describe_instances( 90 | Filters=[ 91 | { 92 | 'Name': 'tag:Name', 93 | 'Values': [instance_name] 94 | }, 95 | { 96 | 'Name': 'instance-state-name', 97 | 'Values': ['running'] 98 | } 99 | ] 100 | ) 101 | 102 | for reservation in response['Reservations']: 103 | for instance in reservation['Instances']: 104 | return instance['InstanceId'] 105 | 106 | return None 107 | 108 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/update_inbound_rules.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import os 4 | from parse_data_from_json import stack_name, directory_name 5 | 6 | """ 7 | This script executes the security group modification, enabling communication between the EC2 instance and the Active Directory. 8 | 9 | This script performs the following operations: 10 | 11 | Loads configuration from a 'data.json' file, including the Active Directory domain name and EC2 instance identifier. 12 | 13 | Defines a function 'add_security_group_to_instance' that: 14 | a. Retrieves the AWS Directory Service details for the specified directory. 15 | b. Identifies the security group associated with the directory. 16 | c. Adds an inbound rule to the instance's security group, allowing all traffic from the directory's security group. 17 | 18 | """ 19 | 20 | 21 | instance_name = stack_name + "/MyAutoScalingGroup" 22 | 23 | def add_security_group_to_instance(directory_name, instance_name): 24 | try: 25 | ds = boto3.client('ds') 26 | ec2 = boto3.client('ec2') 27 | 28 | directories = ds.describe_directories()['DirectoryDescriptions'] 29 | directory = next((d for d in directories if d['Name'] == directory_name), None) 30 | 31 | if not directory: 32 | raise ValueError(f"Directory '{directory_name}' not found") 33 | 34 | directory_id = directory['DirectoryId'] 35 | print(f"Found directory ID: {directory_id}") 36 | 37 | directory_details = ds.describe_directories(DirectoryIds=[directory_id])['DirectoryDescriptions'][0] 38 | security_group_id = directory_details['VpcSettings']['SecurityGroupId'] 39 | 40 | response = ec2.describe_instances( 41 | Filters=[ 42 | { 43 | 'Name': 'tag:Name', 44 | 'Values': [instance_name] 45 | }, 46 | { 47 | 'Name': 'instance-state-name', 48 | 'Values': ['running'] 49 | } 50 | ] 51 | ) 52 | 53 | if not response['Reservations']: 54 | raise ValueError(f"No instances found with tag:Name '{instance_name}'") 55 | 56 | instances = response['Reservations'][0]['Instances'] 57 | if not instances: 58 | raise ValueError(f"No instances found in the reservation") 59 | 60 | instance = instances[0] 61 | 62 | if 'SecurityGroups' not in instance or not instance['SecurityGroups']: 63 | raise ValueError(f"No security groups found for the instance") 64 | 65 | instance_sg_id = instance['SecurityGroups'][0]['GroupId'] 66 | 67 | # Check if the rule already exists 68 | existing_rules = ec2.describe_security_group_rules( 69 | Filters=[{'Name': 'group-id', 'Values': [instance_sg_id]}] 70 | )['SecurityGroupRules'] 71 | 72 | rule_exists = any( 73 | rule['IpProtocol'] == '-1' and 74 | rule['FromPort'] == -1 and 75 | rule['ToPort'] == -1 and 76 | rule.get('ReferencedGroupInfo', {}).get('GroupId') == security_group_id 77 | for rule in existing_rules 78 | ) 79 | 80 | if rule_exists: 81 | print(f"Rule already exists in security group {instance_sg_id}") 82 | return True 83 | 84 | # Add the new inbound rule to the security group 85 | ec2.authorize_security_group_ingress( 86 | GroupId=instance_sg_id, 87 | IpPermissions=[ 88 | { 89 | 'IpProtocol': '-1', # All traffic 90 | 'FromPort': -1, # All ports 91 | 'ToPort': -1, # All ports 92 | 'UserIdGroupPairs': [{'GroupId': security_group_id}] 93 | } 94 | ] 95 | ) 96 | print(f"Successfully added inbound rule to security group {instance_sg_id}") 97 | return True 98 | 99 | except Exception as e: 100 | print(f"An error occurred: {str(e)}") 101 | return False 102 | -------------------------------------------------------------------------------- /cdk/cdk-domainless-mode/tests/update_task_def_and_run_tasks.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import os 4 | 5 | """ 6 | AWS ECS Task Definition Update and Execution Script 7 | 8 | This script automates the process of updating and running ECS (Elastic Container Service) tasks. It performs the following operations: 9 | 10 | Task Definition Management: 11 | 12 | Identifies all task definition families matching a specified pattern. 13 | For each task definition family: 14 | a. Updates the task definition with a new Docker image URI from ECR. 15 | b. Registers a new revision of the task definition with ECS. 16 | 17 | Task Execution: 18 | 19 | For each updated task definition: 20 | a. Attempts to run a new task in the specified ECS cluster. 21 | b. Configures the task with the appropriate network settings (subnets and security group). 22 | 23 | """ 24 | 25 | def update_task_definition_image(task_definition_family, repository_name, tag, region): 26 | ecs_client = boto3.client('ecs', region_name=region) 27 | ecr_client = boto3.client('ecr', region_name=region) 28 | response = ecr_client.describe_repositories(repositoryNames=[repository_name]) 29 | repository_uri = response['repositories'][0]['repositoryUri'] 30 | image_uri = f"{repository_uri}:{tag}" 31 | 32 | # Get the current task definition 33 | response = ecs_client.describe_task_definition(taskDefinition=task_definition_family) 34 | task_definition = response['taskDefinition'] 35 | 36 | # Update the container definition with the new image URI 37 | container_definitions = task_definition['containerDefinitions'] 38 | for container in container_definitions: 39 | if container['name'] == 'MyContainer': 40 | container['image'] = image_uri 41 | 42 | # Prepare arguments for register_task_definition 43 | register_args = { 44 | 'family': task_definition['family'], 45 | 'taskRoleArn': task_definition['taskRoleArn'], 46 | 'executionRoleArn': task_definition['executionRoleArn'], 47 | 'networkMode': task_definition['networkMode'], 48 | 'containerDefinitions': container_definitions, 49 | 'volumes': task_definition.get('volumes', []), 50 | 'placementConstraints': task_definition.get('placementConstraints', []), 51 | 'requiresCompatibilities': task_definition['requiresCompatibilities'], 52 | 'cpu': task_definition['cpu'], 53 | 'memory': task_definition['memory'], 54 | } 55 | 56 | if 'tags' in task_definition and task_definition['tags']: 57 | register_args['tags'] = task_definition['tags'] 58 | 59 | # Register the new task definition and get the revised ARN 60 | new_task_definition = ecs_client.register_task_definition(**register_args) 61 | 62 | revised_arn = new_task_definition['taskDefinition']['taskDefinitionArn'] 63 | 64 | print(f"New task definition registered for {task_definition_family}") 65 | print(f"Revised ARN: {revised_arn}") 66 | 67 | return revised_arn 68 | 69 | def run_task(ecs_client, cluster_name, task_definition, subnet_ids, security_group_id): 70 | 71 | try: 72 | task_def_description = ecs_client.describe_task_definition(taskDefinition=task_definition) 73 | container_defs = task_def_description['taskDefinition']['containerDefinitions'] 74 | 75 | # Check if any container in the task definition has a credentialSpecs field 76 | has_cred_specs = any('credentialSpecs' in container and container['credentialSpecs'] 77 | for container in container_defs) 78 | 79 | if not has_cred_specs: 80 | print(f"Skipping task definition {task_definition} as it does not have credentialSpecs") 81 | return None 82 | 83 | task = ecs_client.run_task( 84 | cluster=cluster_name, 85 | taskDefinition=task_definition, 86 | count=1, 87 | launchType='EC2', 88 | networkConfiguration={ 89 | 'awsvpcConfiguration': { 90 | 'subnets': subnet_ids, 91 | 'securityGroups': [security_group_id], 92 | } 93 | } 94 | ) 95 | print(f"Started task: {json.dumps(task['tasks'][0]['taskArn'], default=str)}") 96 | return task['tasks'][0]['taskArn'] 97 | except ecs_client.exceptions.ClientException as e: 98 | print(f"Error starting task for {task_definition}: {str(e)}") 99 | return None 100 | 101 | def get_task_definition_families(ecs_client, pattern): 102 | paginator = ecs_client.get_paginator('list_task_definitions') 103 | task_families = set() 104 | 105 | for page in paginator.paginate(): 106 | for arn in page['taskDefinitionArns']: 107 | if pattern in arn: 108 | family = arn.split('/')[1].split(':')[0] 109 | task_families.add(family) 110 | 111 | return list(task_families) 112 | 113 | 114 | -------------------------------------------------------------------------------- /code_coverage_using_gcov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | mkdir -p build && cd build 4 | if [ $? -ne 0 ]; then 5 | echo "ERROR: Failed at creating build dir" 6 | exit 1 7 | fi 8 | cmake3 .. -DCODE_COVERAGE=ON && make 9 | if [ $? -ne 0 ]; then 10 | echo "ERROR: Failed at build" 11 | exit 1 12 | fi 13 | 14 | echo "Start running the credentials-fetcher daemon" 15 | echo "After running gRPC clients, you must exit the credentials-fetcher daemon using 'touch /tmp/credentials_fetcher_exit.txt'" 16 | echo "Note: If you use ctrl-c or sigkill, the gcda files will not get created" 17 | 18 | echo "Run the following in the build directory:" 19 | 20 | echo "\tpip install gcovr" 21 | echo "\tgcovr --html-details coverage.html -r .." 22 | echo "\ttar cvfz coverage.tar.gz *.html" 23 | echo "To view code coverage, extract coverage.tar.gz and start browsing at coverage.html" 24 | exit 0 25 | -------------------------------------------------------------------------------- /common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | cmake_minimum_required(VERSION 2.8) 4 | -------------------------------------------------------------------------------- /common/constants.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | // renew the ticket 1 hrs before the expiration 4 | #define RENEW_TICKET_HOURS 1 5 | #define SECONDS_IN_HOUR 3600 6 | // Active Directory uses NetBIOS computer names that do not exceed 15 characters. 7 | // https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou 8 | #define HOST_NAME_LENGTH_LIMIT 15 9 | 10 | /* Environment variables in /etc/ecs/ecs.config or shell */ 11 | #define ENV_CF_GMSA_OU "CF_GMSA_OU" 12 | #define ENV_CF_GMSA_SECRET_NAME "CREDENTIALS_FETCHER_SECRET_NAME_FOR_DOMAINLESS_GMSA" 13 | #define ENV_CF_DOMAIN_CONTROLLER "DOMAIN_CONTROLLER_GMSA" 14 | #define ENV_CF_DISTINGUISHED_NAME "CF_GMSA_DISTINGUISHED_NAME" 15 | 16 | extern "C" int my_kinit_main(int, char **); 17 | -------------------------------------------------------------------------------- /configuration/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | FILE(GLOB SRC_FILES src/*.cpp) 4 | 5 | file( 6 | COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/krb5.conf 7 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../ 8 | FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 9 | ) 10 | 11 | set(configuration "${SRC_FILES}" PARENT_SCOPE) 12 | 13 | enable_testing() 14 | -------------------------------------------------------------------------------- /configuration/config.h.in: -------------------------------------------------------------------------------- 1 | #cmakedefine CF_KRB_DIR "@CF_KRB_DIR@" 2 | #cmakedefine CF_UNIX_DOMAIN_SOCKET_DIR "@CF_UNIX_DOMAIN_SOCKET_DIR@" 3 | #cmakedefine CF_LOGGING_DIR "@CF_LOGGING_DIR@" 4 | #cmakedefine CF_TEST_DOMAIN_NAME "@CF_TEST_DOMAIN_NAME@" 5 | #cmakedefine CF_TEST_GMSA_ACCOUNT "@CF_TEST_GMSA_ACCOUNT@" 6 | #cmakedefine CMAKE_PROJECT_VERSION "@CMAKE_PROJECT_VERSION@" 7 | 8 | // Integration Test constants 9 | #cmakedefine CF_TEST_ACCESS_KEY_ID "@CF_TEST_ACCESS_KEY_ID@" 10 | #cmakedefine CF_TEST_SECRET_ACCESS_KEY "@CF_TEST_SECRET_ACCESS_KEY@" 11 | #cmakedefine CF_TEST_SESSION_TOKEN "@CF_TEST_SESSION_TOKEN@" 12 | #cmakedefine CF_TEST_REGION "@CF_TEST_REGION@" 13 | #cmakedefine CF_TEST_USERNAME "@CF_TEST_USERNAME@" 14 | #cmakedefine CF_TEST_PASSWORD "@CF_TEST_PASSWORD@" 15 | #cmakedefine CF_TEST_CREDSPEC_ARN "@CF_TEST_CREDSPEC_ARN@" 16 | -------------------------------------------------------------------------------- /configuration/src/config.cpp: -------------------------------------------------------------------------------- 1 | #include "daemon.h" 2 | #include "util.hpp" 3 | 4 | /** 5 | * This function prints formatted help descriptions 6 | * @param long_options - option struct passed to getopt_log method 7 | * @param long_options - map of commnad line arg and its description 8 | */ 9 | void print_help( const struct option* long_options, 10 | const std::map& options_descriptions ) 11 | { 12 | std::cout << "Usage: " << std::endl; 13 | std::cout << "Runtime Environment Variables:" << std::endl; 14 | std::cout << "CF_CRED_SPEC_FILE=:" << std::endl; 15 | std::cout << "\t\tSet to a path of a json credential file." << std::endl; 16 | std::cout << "\t\tUse an optional colon followed by a lease identifier (Default: " 17 | << DEFAULT_CRED_FILE_LEASE_ID << ")" << std::endl; 18 | std::cout << "\nAllowed options" << std::endl; 19 | size_t max_option_length = 0; 20 | for ( const struct option* opt = long_options; opt->name != nullptr; ++opt ) 21 | { 22 | size_t option_length = 23 | opt->has_arg == required_argument ? strlen( opt->name ) + 5 : strlen( opt->name ) + 2; 24 | if ( option_length > max_option_length ) 25 | { 26 | max_option_length = option_length; 27 | } 28 | } 29 | for ( const struct option* opt = long_options; opt->name != nullptr; ++opt ) 30 | { 31 | std::string opt_string = std::string( "--" ) + opt->name; 32 | std::string description; 33 | if ( options_descriptions.count( opt->name ) > 0 ) 34 | { 35 | description = options_descriptions.at( opt->name ); 36 | } 37 | else 38 | { 39 | description = opt->name; 40 | } 41 | std::cout << " " << std::left << std::setw( max_option_length ) << opt_string << "\t" 42 | << description << std::endl; 43 | } 44 | } 45 | 46 | /** 47 | * This function has options used to invoke the daemon such as 48 | * credentials-fetcherd --configfile path-to-file 49 | * @param argc - argc from command line input 50 | * @param argv - argv from command line input 51 | * @param cf_daemon - credentials fetcher parent object 52 | * @return status - 0 if successful 53 | */ 54 | 55 | int parse_options( int argc, const char* argv[], Daemon& cf_daemon ) 56 | { 57 | try 58 | { 59 | std::string domainless_gmsa_field( "CREDENTIALS_FETCHER_SECRET_NAME_FOR_DOMAINLESS_GMSA" ); 60 | struct option long_options[] = { { "help", no_argument, nullptr, 'h' }, 61 | { "self_test", no_argument, nullptr, 't' }, 62 | { "verbosity", required_argument, nullptr, 'v' }, 63 | { "aws_sm_secret_name", required_argument, nullptr, 's' }, 64 | { "version", no_argument, nullptr, 'n' }, 65 | { "healthcheck", no_argument, nullptr, 'c' }, 66 | { nullptr, 0, nullptr, 0 } }; 67 | std::map options_descriptions{ 68 | { "help", "produce help message" }, 69 | { "self_test", "Run tests such as utf16 decode" }, 70 | { "verbosity", "set verbosity level" }, 71 | { "aws_sm_secret_name", "Name of secret containing username/password in AWS Secrets " 72 | "Manager (in same region)" }, 73 | { "healthcheck", "health of credentials-fetcher" }, 74 | { "version", "Version of credentials-fetcher" } }; 75 | int option; 76 | int healthCheckResponse; 77 | while ( ( option = getopt_long( argc, (char* const*)argv, "htv:s:n", long_options, nullptr ) ) != -1 ) 78 | { 79 | switch ( option ) 80 | { 81 | case 'h': 82 | print_help( long_options, options_descriptions ); 83 | return EXIT_FAILURE; 84 | case 't': 85 | std::cout << "run diagnostic set" << std::endl; 86 | cf_daemon.run_diagnostic = true; 87 | break; 88 | case 'v': 89 | break; 90 | case 's': 91 | cf_daemon.aws_sm_secret_name = optarg; 92 | break; 93 | case 'n': 94 | std::cout << CMAKE_PROJECT_VERSION << std::endl; 95 | return EXIT_FAILURE; 96 | case 'c': 97 | healthCheckResponse = HealthCheck("test"); 98 | std::cout << healthCheckResponse << std::endl; 99 | if(healthCheckResponse != 0) 100 | { 101 | exit(EXIT_FAILURE); 102 | } 103 | else 104 | { 105 | exit(EXIT_SUCCESS); 106 | } 107 | 108 | default: 109 | std::cout << "Run with --help to see options" << std::endl; 110 | return EXIT_FAILURE; 111 | } 112 | } 113 | 114 | if ( cf_daemon.aws_sm_secret_name.empty() ) 115 | { 116 | cf_daemon.aws_sm_secret_name = Util::retrieve_variable_from_ecs_config(domainless_gmsa_field); 117 | } 118 | } 119 | catch ( const std::exception& ex ) 120 | { 121 | std::cout << "Run with --help to see options" << std::endl; 122 | return EXIT_FAILURE; 123 | } 124 | 125 | return EXIT_SUCCESS; 126 | } 127 | -------------------------------------------------------------------------------- /configuration/src/krb5.conf: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | includedir /etc/krb5.conf.d/ 5 | 6 | [logging] 7 | default = STDERR 8 | 9 | [libdefaults] 10 | dns_lookup_realm = true 11 | dns_lookup_kdc = true 12 | forwardable = true 13 | rdns = false 14 | default_ccache_name = FILE:/var/scratch/krbcache 15 | default_realm = CONTOSO.COM 16 | 17 | [realms] 18 | # EXAMPLE.COM = { 19 | # kdc = kerberos.example.com 20 | # admin_server = kerberos.example.com 21 | # } 22 | 23 | [domain_realm] 24 | # .example.com = EXAMPLE.COM 25 | # example.com = EXAMPLE.COM -------------------------------------------------------------------------------- /daemon/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | FILE(GLOB SRC_FILES src/*.cpp) 4 | 5 | set(daemon "${SRC_FILES}" PARENT_SCOPE) 6 | 7 | enable_testing() 8 | -------------------------------------------------------------------------------- /dependencies/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(build_binaries) 4 | 5 | add_custom_target(build_krb5) 6 | add_custom_command(TARGET build_krb5 7 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 8 | COMMAND rm -rf ${CMAKE_BINARY_DIR}/krb5 9 | COMMAND git clone https://github.com/krb5/krb5.git -b krb5-1.21.2-final 10 | COMMAND cd krb5/src && autoreconf && ./configure && make -j && sudo make install 11 | COMMENT "Compiling krb5-1.21.2-final" 12 | VERBATIM) 13 | 14 | add_custom_target(build_grpc) 15 | add_custom_command(TARGET build_grpc 16 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 17 | COMMAND rm -rf ${CMAKE_BINARY_DIR}/grpc 18 | COMMAND git clone --recurse-submodules -b v1.58.0 https://github.com/grpc/grpc 19 | COMMAND cd grpc && mkdir -p build && cd build && cmake -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DCMAKE_CXX_STANDARD=17 ../ && make -j && sudo make install 20 | COMMENT "Compiling grpc-v1.58" 21 | VERBATIM) 22 | -------------------------------------------------------------------------------- /docs/cf_gmsa_ou.md: -------------------------------------------------------------------------------- 1 | Active Directory administrator can create the GMSA account with any distinguished name format. 2 | 3 | credentials-fetcher uses the GMSA distinguished name format "CN=${GMSA_ACCOUNT_NAME},${CF_GMSA_OU},DC=example,DC=com" where ",DC=example,DC=com" is generated depending on the domain. The environment variable CF_GMSA_OU default value is "CN=Managed Service Accounts". Users should change it to match their directory format. 4 | 5 | For example, GMSA account "BobSponge" in domain "example.com" results in GMSA distinguished name "CN=BobSponge,CN=Managed Service Accounts,DC=example,DC=com". When the user defines CF_GMSA_OU='OU=DA Managed Service Accounts,OU=DA' results in GMSA distinguished name "CN=BobSponge,OU=DA Managed Service Accounts,OU=DA,DC=example,DC=com" 6 | -------------------------------------------------------------------------------- /metadata/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(metadata) 4 | 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") 6 | 7 | FILE(GLOB SRC_FILES src/*.cpp tests/*.cpp) 8 | 9 | set(metadata "${SRC_FILES}" PARENT_SCOPE) 10 | 11 | # test sample for unit tests 12 | file( 13 | COPY ${CMAKE_CURRENT_SOURCE_DIR}/tests/metadata_sample.json 14 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../ 15 | FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 16 | ) 17 | file( 18 | COPY ${CMAKE_CURRENT_SOURCE_DIR}/tests/metadata_invalid_sample.json 19 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../ 20 | FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 21 | ) 22 | 23 | enable_testing () 24 | -------------------------------------------------------------------------------- /metadata/src/metadata.cpp: -------------------------------------------------------------------------------- 1 | #include "daemon.h" 2 | #include 3 | #include 4 | #include 5 | #include "util.hpp" 6 | 7 | static const std::vector invalid_path_characters = { 8 | '&', ':', '\\', '|', '*', '?', '<', '>', '`', '$', '{', '}', '(', ')', '"', ';' }; 9 | /** 10 | * 11 | * @param path - string input that has to be validated 12 | * @return true or false if string contains or not contains invalid characters 13 | */ 14 | bool contains_invalid_characters( const std::string& path ) 15 | { 16 | bool result = false; 17 | // Iterate over all characters in invalid_path_characters vector 18 | for ( const char& ch : invalid_path_characters ) 19 | { 20 | // Check if character exist in string 21 | if ( path.find( ch ) != std::string::npos ) 22 | { 23 | result = true; 24 | break; 25 | } 26 | } 27 | return result; 28 | } 29 | /** 30 | * read the kerberos ticket information from the cache 31 | * @param file_path - file path for the metadata associated to a lease 32 | * @param krb_files_dir - path of the dir for kerberos tickets 33 | * @return vector of kerberos ticket info 34 | */ 35 | std::list read_meta_data_json( std::string file_path ) 36 | { 37 | std::list krb_ticket_info_list; 38 | try 39 | { 40 | if ( file_path.empty() ) 41 | { 42 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: meta data file is empty" << std::endl; 43 | return krb_ticket_info_list; 44 | } 45 | 46 | // deserialize json to krb_ticket_info object 47 | Json::Value root; 48 | std::ifstream json_file( file_path ); 49 | 50 | if ( json_file.is_open() ) 51 | { 52 | json_file >> root; 53 | json_file.close(); 54 | 55 | // deserialize json to krb_ticket_info object 56 | const Json::Value& child_tree_krb_info = root["krb_ticket_info"]; 57 | 58 | for ( const Json::Value& krb_info : child_tree_krb_info ) 59 | { 60 | krb_ticket_info_t * krb_ticket_info = 61 | new krb_ticket_info_t ; 62 | std::string krb_file_path = krb_info["krb_file_path"].asString(); 63 | 64 | if ( contains_invalid_characters( krb_file_path ) ) 65 | { 66 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: krb file path contains invalid characters" << 67 | std::endl; 68 | delete ( krb_ticket_info ); 69 | break; 70 | } 71 | 72 | // only add path if it exists 73 | if ( std::filesystem::exists( krb_file_path ) ) 74 | { 75 | krb_ticket_info->krb_file_path = krb_file_path; 76 | 77 | std::string service_account = krb_info["service_account_name"].asString(); 78 | if (Util::contains_invalid_characters_in_ad_account_name(service_account)) { 79 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: service account name contains invalid characters" << std::endl; 80 | delete krb_ticket_info; 81 | break; 82 | } 83 | krb_ticket_info->service_account_name = service_account; 84 | 85 | krb_ticket_info->domain_name = krb_info["domain_name"].asString(); 86 | krb_ticket_info->domainless_user = krb_info["domainless_user"].asString(); 87 | if(krb_info.isMember("distinguished_name")) 88 | { 89 | krb_ticket_info->distinguished_name = krb_info["distinguished_name"].asString(); 90 | } 91 | 92 | if(krb_info.isMember("credspec_info")) 93 | { 94 | krb_ticket_info->credspec_info = krb_info["credspec_info"].asString(); 95 | } 96 | 97 | krb_ticket_info_list.push_back( krb_ticket_info ); 98 | } 99 | } 100 | } 101 | } 102 | catch ( const std::exception& ex ) 103 | { 104 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: '" << ex.what() << "'!" << std::endl; 105 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: meta data file is not properly formatted" << 106 | std::endl; 107 | return krb_ticket_info_list; 108 | } 109 | 110 | return krb_ticket_info_list; 111 | } 112 | 113 | /** 114 | * write the kerberos ticket information to the cache 115 | * Example meta_file: 116 | * { 117 | "krb_ticket_info": [ 118 | { 119 | "krb_file_path": 120 | "\/usr\/share\/credentials-fetcher\/krbdir\/3e1ff0bb9f966192c440\/ccname_WebApp01_OPMsbZ", 121 | "service_account_name": "WebApp01", 122 | "domain_name": "contoso.com", 123 | "domainless_user": "user1", 124 | "distinguished_name": "CN=webapp01,CN=Managed Service Accounts,DC=contoso,DC=com" 125 | } 126 | ] 127 | } 128 | 129 | * @param krb_ticket_info - info of the kerberos ticket to create 130 | * @param lease_id - lease_id associated to the kerberos ticket created 131 | * @param krb_files_dir - path of the dir for kerberos ticket 132 | * @return 0 or 1 for successful or failed writes 133 | */ 134 | 135 | int write_meta_data_json( krb_ticket_info_t* krb_ticket_info, 136 | std::string lease_id, std::string krb_files_dir ) 137 | { 138 | std::list krb_ticket_info_list; 139 | 140 | krb_ticket_info_list.push_back(krb_ticket_info); 141 | 142 | return write_meta_data_json(krb_ticket_info_list, lease_id, krb_files_dir); 143 | } 144 | 145 | /* @param krb_ticket_info_list - info of the kerberos tickets created 146 | * @param lease_id - lease_id associated to the kerberos tickets created 147 | * @param krb_files_dir - path of the dir for kerberos tickets 148 | * @return 0 or 1 for successful or failed writes 149 | */ 150 | int write_meta_data_json( std::list krb_ticket_info_list, 151 | std::string lease_id, std::string krb_files_dir ) 152 | { 153 | try 154 | { 155 | std::string meta_file_name = lease_id + "_metadata.json"; 156 | std::string file_path = krb_files_dir + "/" + lease_id + "/" + meta_file_name; 157 | 158 | // create the meta file in the lease directory 159 | std::filesystem::path dirPath( file_path ); 160 | std::filesystem::create_directories( dirPath.parent_path() ); 161 | 162 | // parse the kerberos info and serialize to json 163 | Json::Value root; 164 | Json::Value krb_ticket_info_parent; 165 | 166 | for ( auto krb_ticket_info : krb_ticket_info_list ) 167 | { 168 | Json::Value ticket_info; 169 | ticket_info["krb_file_path"] = krb_ticket_info->krb_file_path; 170 | ticket_info["service_account_name"] = krb_ticket_info->service_account_name; 171 | ticket_info["domain_name"] = krb_ticket_info->domain_name; 172 | ticket_info["domainless_user"] = krb_ticket_info->domainless_user; 173 | ticket_info["credspec_info"] = krb_ticket_info->credspec_info; 174 | ticket_info["distinguished_name"] = krb_ticket_info->distinguished_name; 175 | 176 | krb_ticket_info_parent.append( ticket_info ); 177 | } 178 | 179 | root["krb_ticket_info"] = krb_ticket_info_parent; 180 | 181 | Json::StreamWriterBuilder writer; 182 | std::string jsonString = Json::writeString( writer, root ); 183 | std::ofstream json_file( file_path ); 184 | if ( json_file.is_open() ) 185 | { 186 | json_file << jsonString; 187 | json_file.close(); 188 | } 189 | else 190 | { 191 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: Failed to write JSON file: " << file_path << std::endl; 192 | } 193 | } 194 | catch ( const std::exception& ex ) 195 | { 196 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: '" << ex.what() << "'!" << std::endl; 197 | std::cout << Util::getCurrentTime() << '\t' << "ERROR: failed to write meta data file" << 198 | std::endl; 199 | return -1; 200 | } 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /metadata/tests/metadata_invalid_sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "krb_ticket_info": [ 3 | { 4 | "krb_file_path": "\/usr\/share\/credentials-fetcher\/krbdir\/73099acdb5807b4bbf91\/ccname_WebApp01_7K4PEM && test", 5 | "service_account_name": "WebApp01", 6 | "domain_name": "contoso.com" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /metadata/tests/metadata_sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "krb_ticket_info": [ 3 | { 4 | "krb_file_path": "\/usr\/share\/credentials-fetcher\/krbdir\/73099acdb5807b4bbf91\/ccname_WebApp01_7K4PEM", 5 | "service_account_name": "WebApp01", 6 | "domain_name": "contoso.com", 7 | "domainless_user": "user1" 8 | }, 9 | { 10 | "krb_file_path": "\/usr\/share\/credentials-fetcher\/krbdir\/73099acdb5807b4bbf91\/ccname_WebApp03_53Yg4I", 11 | "service_account_name": "WebApp03", 12 | "domain_name": "contoso.com", 13 | "domainless_user": "user2" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /metadata/tests/metadata_test.cpp: -------------------------------------------------------------------------------- 1 | #include "daemon.h" 2 | #include 3 | #include 4 | 5 | int read_meta_data_json_test() 6 | { 7 | std::string metadata_file_path = "metadata_sample.json"; 8 | 9 | std::vector paths = { 10 | "/usr/share/credentials-fetcher/krbdir/73099acdb5807b4bbf91/ccname_WebApp01_7K4PEM", 11 | "/usr/share/credentials-fetcher/krbdir/73099acdb5807b4bbf91/ccname_WebApp03_53Yg4I" }; 12 | 13 | for ( auto file_path : paths ) 14 | { 15 | // create the meta file in the lease directory 16 | std::filesystem::path dirPath( file_path ); 17 | std::filesystem::create_directories( dirPath.parent_path() ); 18 | 19 | if ( !std::filesystem::exists( file_path ) ) 20 | { 21 | std::ofstream file( file_path ); 22 | file.close(); 23 | } 24 | } 25 | 26 | std::list result = read_meta_data_json( metadata_file_path ); 27 | 28 | if ( result.empty() || result.size() != 2 ) 29 | { 30 | std::cout << "reading meta data file test is failed" << std::endl; 31 | for ( auto file_path : paths ) 32 | { 33 | std::filesystem::remove_all(file_path ); 34 | } 35 | return EXIT_FAILURE; 36 | } 37 | 38 | std::cout << "read meta data file test is successful" << std::endl; 39 | for ( auto file_path : paths ) 40 | { 41 | std::filesystem::remove_all(file_path ); 42 | } 43 | return EXIT_SUCCESS; 44 | } 45 | 46 | int read_meta_data_invalid_json_test() 47 | { 48 | std::string metadata_file_path = "metadata_invalid_sample.json"; 49 | 50 | std::list result = read_meta_data_json( metadata_file_path ); 51 | 52 | if ( result.empty() ) 53 | { 54 | std::cout << "\nread invalid metadata test is successful" << std::endl; 55 | return EXIT_SUCCESS; 56 | } 57 | std::cout << "\nread invalid metadata test is failed" << std::endl; 58 | return EXIT_FAILURE; 59 | } 60 | 61 | int write_meta_data_json_test() 62 | { 63 | std::string metadata_file_path = "metadata_sample.json"; 64 | 65 | std::vector paths = { 66 | "/usr/share/credentials-fetcher/krbdir/73099acdb5807b4bbf91/ccname_WebApp01_7K4PEM", 67 | "/usr/share/credentials-fetcher/krbdir/73099acdb5807b4bbf91/ccname_WebApp03_53Yg4I" }; 68 | 69 | for ( auto file_path : paths ) 70 | { 71 | // create the meta file in the lease directory 72 | std::filesystem::path dirPath( file_path ); 73 | std::filesystem::create_directories( dirPath.parent_path() ); 74 | 75 | if ( !std::filesystem::exists( file_path ) ) 76 | { 77 | std::ofstream file( file_path ); 78 | file.close(); 79 | } 80 | } 81 | 82 | std::list test_ticket_info = 83 | read_meta_data_json( metadata_file_path ); 84 | 85 | std::string krb_files_dir = "/usr/share/credentials-fetcher/krbdir"; 86 | std::string test_lease_id = "test1234567890"; 87 | 88 | int result = write_meta_data_json( test_ticket_info, test_lease_id, krb_files_dir ); 89 | 90 | if ( result != 0 ) 91 | { 92 | std::cout << "write meta data to file test is failed" << std::endl; 93 | for ( auto file_path : paths ) 94 | { 95 | std::filesystem::remove_all(file_path ); 96 | } 97 | return EXIT_FAILURE; 98 | } 99 | 100 | // finally delete test lease directory 101 | std::filesystem::remove_all( krb_files_dir + "/" + test_lease_id ); 102 | 103 | std::cout << "write meta data info to file test is successful" << std::endl; 104 | for ( auto file_path : paths ) 105 | { 106 | std::filesystem::remove_all(file_path ); 107 | } 108 | return EXIT_SUCCESS; 109 | } 110 | -------------------------------------------------------------------------------- /package/credentials-fetcher.spec: -------------------------------------------------------------------------------- 1 | %global major_version 1 2 | %global minor_version 3 3 | %global patch_version 8 4 | 5 | # For handling bump release by rpmdev-bumpspec and mass rebuild 6 | %global baserelease 0 7 | 8 | Name: credentials-fetcher 9 | Version: %{major_version}.%{minor_version}.%{patch_version} 10 | Release: %{baserelease}%{?dist} 11 | Summary: credentials-fetcher is a daemon that refreshes tickets or tokens periodically 12 | 13 | License: Apache-2.0 14 | URL: https://github.com/aws/credentials-fetcher 15 | Source0: credentials-fetcher-v.1.3.8.tar.gz 16 | 17 | BuildRequires: cmake3 make chrpath openldap-clients grpc-devel gcc-c++ glib2-devel jsoncpp-devel 18 | BuildRequires: openssl-devel zlib-devel protobuf-devel re2-devel krb5-devel systemd-devel 19 | BuildRequires: systemd-rpm-macros grpc-plugins 20 | 21 | %if 0%{?amzn} >= 2023 22 | BuildRequires: aws-sdk-cpp-devel aws-sdk-cpp aws-sdk-cpp-static 23 | %else 24 | BuildRequires: awscli 25 | %endif 26 | 27 | BuildRequires: dotnet-sdk-8.0 28 | Requires: dotnet-runtime-8.0 29 | 30 | Requires: bind-utils openldap openldap-clients jsoncpp 31 | # No one likes you i686 32 | ExclusiveArch: x86_64 aarch64 s390x 33 | 34 | # https://docs.fedoraproject.org/en-US/packaging-guidelines/CMake/ 35 | 36 | %description 37 | This daemon creates and refreshes kerberos tickets, these 38 | tickets can be used to launch new containers. 39 | The gMSA feature can be implemented using this daemon. 40 | Kerberos tickets are refreshed when tickets expire 41 | or when a gMSA password changes. 42 | The same method can be used to refresh other types of security tokens. 43 | This spec file is specific to Fedora, use this file to rpmbuild on Fedora. 44 | 45 | %prep 46 | %setup -q -n credentials-fetcher-v.%{version} 47 | # abseil-cpp LTS 20230125 requires at least C++14; string_view requires C++17: 48 | sed -r -i 's/(std=c\+\+)11/\117/' CMakeLists.txt 49 | 50 | %build 51 | %cmake3 52 | %cmake_build 53 | %install 54 | 55 | %cmake_install 56 | # https://docs.fedoraproject.org/en-US/packaging-guidelines/#_removing_rpath 57 | # https://docs.fedoraproject.org/en-US/packaging-guidelines/#_rpath_for_internal_libraries 58 | chrpath --delete %{buildroot}/%{_sbindir}/credentials-fetcherd 59 | 60 | %check 61 | # TBD: Run tests from top-level directory 62 | ctest3 63 | 64 | %files 65 | %{_sbindir}/credentials-fetcherd 66 | %{_unitdir}/credentials-fetcher.service 67 | %license LICENSE 68 | # https://docs.fedoraproject.org/en-US/packaging-guidelines/LicensingGuidelines/ 69 | %doc CONTRIBUTING.md NOTICE README.md 70 | %attr(0700, -, -) %{_sbindir}/credentials_fetcher_utf16_private.exe 71 | %attr(0700, -, -) %{_sbindir}/credentials_fetcher_utf16_private.runtimeconfig.json 72 | %attr(0755, -, -) %{_sbindir}/krb5.conf 73 | 74 | %changelog 75 | 76 | * Mon Mar 10 2025 Anushka Srinivasa - 1.3.8 77 | - Fix for intermittent grpc server shutdown issue 78 | - Moving watchdog heartbeats into its own pthread 79 | - Conditionally add build dependency awscli 80 | 81 | * Fri Jan 17 2025 Samiullah Mohammed - 1.3.7 82 | - DNS and associated retries 83 | - Complex DN support 84 | 85 | * Mon Jan 29 2024 Sai Kiran Akula - 1.3.6 86 | - Create 1.3.6 release, added input validation 87 | 88 | * Mon Jan 15 2024 Sai Kiran Akula - 1.3.5 89 | - Create 1.3.5 release, added input validation 90 | - Flag to handle unit tests 91 | 92 | * Mon Dec 25 2023 Sai Kiran Akula - 1.3.4 93 | - Create 1.3.4 release, fix to skip directory check 94 | - create custom mount points 95 | 96 | * Tue Dec 12 2023 Sai Kiran Akula - 1.3.3 97 | - Fixes to add validation 98 | - Add option to take custom paths 99 | 100 | * Mon Dec 4 2023 Sai Kiran Akula - 1.3.1 101 | - Create 1.3.1 release, add file based loggin support 102 | - Bug fixes for renewal logic 103 | 104 | * Tue Nov 7 2023 Sai Kiran Akula - 1.3.0 105 | - Create 1.3.0 release, add support to retrieve credspec from s3 106 | - Added dependency for aws-sdk-cpp 107 | 108 | * Thu Aug 31 2023 Tom Callaway - 1.2.0-3 109 | - rebuild for abseil 110 | 111 | * Wed Jul 19 2023 Fedora Release Engineering - 1.2.0-2 112 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild 113 | 114 | * Mon May 15 2023 Sai Kiran Akula - 1.2.0 115 | - Create 1.2.0 release 116 | 117 | * Thu Mar 23 2023 Tom Callaway - 1.1.0-7 118 | - rebuild for new abseil-cpp 119 | 120 | * Tue Mar 07 2023 Benjamin A. Beasley - 1.1.0-6 121 | - Build as C++14, required by abseil-cpp 20230125 122 | 123 | * Thu Feb 23 2023 Tom Callaway - 1.1.0-5 124 | - fix build against boost 1.81 (bz2172636) 125 | 126 | * Mon Feb 20 2023 Jonathan Wakely - 1.1.0-4 127 | - Rebuilt for Boost 1.81 128 | 129 | * Thu Feb 09 2023 Benjamin A. Beasley - 1.1.0-3 130 | - Depend on dotnet-sdk-7.0; there is no longer an unversioned “dotnet” package 131 | - Restore ppc64le support 132 | 133 | * Thu Jan 19 2023 Fedora Release Engineering - 1.1.0-2 134 | - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild 135 | 136 | * Thu Oct 27 2022 Sai Kiran Akula - 1.1.0 137 | - Create 1.1 release 138 | 139 | * Mon Oct 24 2022 Samiullah Mohammed - 1.0.0 140 | - Add domainless gmsa 141 | 142 | * Wed Oct 12 2022 Sai Kiran Akula - 1.0.0 143 | - Create 1.0 release 144 | 145 | * Mon Sep 19 2022 Tom Callaway - 0.0.94-2 146 | - rebuild for rawhide 147 | 148 | * Sat Sep 10 2022 Samiullah Mohammed - 0.0.94-1 149 | - Replace mono with dotnet 150 | 151 | * Mon Aug 29 2022 Tom Callaway - 0.0.94-1 152 | - systemd clean up 153 | 154 | * Mon Aug 22 2022 Sai Kiran Akula - 0.0.93 155 | - Add validation for read metadata file and rpm install require openldap-clients 156 | 157 | * Wed Aug 10 2022 Samiullah Mohammed - 0.0.92 158 | - Move binaries to standard Linux directories 159 | 160 | - Add directory paths as configurable variables in cmake 161 | - Generate systemd service file from cmake 162 | 163 | * Sun Aug 7 2022 Samiullah Mohammed - 0.0.91 164 | - Relocate binary, library files and change permissions 165 | 166 | * Sat Jul 30 2022 Samiullah Mohammed - 0.0.90 167 | - add ctests and bump revision to 0.0.90 168 | 169 | * Thu Jul 28 2022 Samiullah Mohammed - 0.0.1 170 | - Add mono-based utf16 decoder 171 | 172 | * Tue Jul 12 2022 Samiullah Mohammed - 0.0.1 173 | - Resolve rpath for Fedora and change macros 174 | 175 | * Sat Jun 18 2022 Sai Kiran Akula - 0.0.1 176 | - Refactor cmake for all the directories 177 | 178 | * Thu Jun 16 2022 Samiullah Mohammed - 0.0.1 179 | - Compile subdirectory into a shared library 180 | 181 | * Wed Jun 15 2022 Samiullah Mohammed - 0.0.1 182 | - Add daemon infra 183 | 184 | * Wed Jun 8 2022 Samiullah Mohammed - 0.0.1 185 | - Fixes to rpm spec 186 | 187 | * Mon Jun 6 2022 Samiullah Mohammed - 0.0.1 188 | - Initial commit -------------------------------------------------------------------------------- /protos/credentialsfetcher.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package credentialsfetcher; 4 | 5 | service CredentialsFetcherService { 6 | rpc AddKerberosLease (CreateKerberosLeaseRequest) returns (CreateKerberosLeaseResponse); 7 | rpc AddNonDomainJoinedKerberosLease 8 | (CreateNonDomainJoinedKerberosLeaseRequest) 9 | returns (CreateNonDomainJoinedKerberosLeaseResponse); 10 | rpc RenewNonDomainJoinedKerberosLease 11 | (RenewNonDomainJoinedKerberosLeaseRequest) returns (RenewNonDomainJoinedKerberosLeaseResponse); 12 | rpc DeleteKerberosLease (DeleteKerberosLeaseRequest) returns (DeleteKerberosLeaseResponse); 13 | rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse); 14 | rpc AddKerberosArnLease (KerberosArnLeaseRequest) returns (CreateKerberosArnLeaseResponse); 15 | rpc RenewKerberosArnLease (RenewKerberosArnLeaseRequest) returns (RenewKerberosArnLeaseResponse); 16 | } 17 | 18 | message HealthCheckRequest { 19 | string service = 1; 20 | } 21 | 22 | message HealthCheckResponse { 23 | string status = 1; 24 | } 25 | 26 | message KerberosArnLeaseRequest { 27 | repeated string credspec_arns = 1; 28 | string access_key_id = 2; 29 | string secret_access_key = 3; 30 | string session_token = 4; 31 | string region = 5; 32 | } 33 | 34 | message RenewKerberosArnLeaseRequest { 35 | string access_key_id = 1; 36 | string secret_access_key = 2; 37 | string session_token = 3; 38 | string region = 4; 39 | } 40 | 41 | message CreateKerberosArnLeaseResponse { 42 | string lease_id = 1; 43 | repeated KerberosTicketArnResponse krb_ticket_response_map = 2; 44 | } 45 | 46 | message RenewKerberosArnLeaseResponse { 47 | string status = 1; 48 | } 49 | 50 | message KerberosTicketArnResponse { 51 | string credspec_arns = 1; 52 | string created_kerberos_file_paths = 2; 53 | } 54 | 55 | message CreateKerberosLeaseRequest { 56 | repeated string credspec_contents = 1; 57 | } 58 | 59 | message CreateKerberosLeaseResponse { 60 | string lease_id = 1; 61 | repeated string created_kerberos_file_paths = 2; 62 | } 63 | 64 | message CreateNonDomainJoinedKerberosLeaseRequest{ 65 | repeated string credspec_contents = 1; 66 | string username = 2; 67 | string password = 3; 68 | string domain = 4; 69 | } 70 | 71 | message CreateNonDomainJoinedKerberosLeaseResponse{ 72 | string lease_id = 1; 73 | repeated string created_kerberos_file_paths = 2; 74 | } 75 | 76 | message RenewNonDomainJoinedKerberosLeaseRequest{ 77 | string username = 1; 78 | string password = 2; 79 | string domain = 3; 80 | } 81 | 82 | message RenewNonDomainJoinedKerberosLeaseResponse{ 83 | repeated string renewed_kerberos_file_paths = 1; 84 | } 85 | 86 | 87 | message DeleteKerberosLeaseRequest { 88 | string lease_id = 1; 89 | } 90 | 91 | message DeleteKerberosLeaseResponse { 92 | string lease_id = 1; 93 | repeated string deleted_kerberos_file_paths = 2; 94 | } -------------------------------------------------------------------------------- /renewal/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(renewal) 4 | 5 | FILE(GLOB SRC_FILES src/*.cpp tests/*.cpp) 6 | 7 | set(renewal "${SRC_FILES}" PARENT_SCOPE) 8 | 9 | enable_testing () 10 | -------------------------------------------------------------------------------- /renewal/src/renewal.cpp: -------------------------------------------------------------------------------- 1 | #include "daemon.h" 2 | #include "util.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | int krb_ticket_renew_handler( Daemon cf_daemon ) 8 | { 9 | std::string krb_files_dir = cf_daemon.krb_files_dir; 10 | int interval = cf_daemon.krb_ticket_handle_interval; 11 | CF_logger cf_logger = cf_daemon.cf_logger; 12 | 13 | if ( krb_files_dir.empty() ) 14 | { 15 | fprintf( stderr, SD_CRIT "directory path for kerberos tickets is not provided" ); 16 | return -1; 17 | } 18 | 19 | while ( !cf_daemon.got_systemd_shutdown_signal ) 20 | { 21 | try 22 | { 23 | auto x = std::chrono::steady_clock::now() + std::chrono::minutes( interval ); 24 | std::this_thread::sleep_until( x ); 25 | std::cout << Util::getCurrentTime() << '\t' << "INFO: renewal started" << std::endl; 26 | 27 | // identify the metadata files in the krb directory 28 | std::vector metadatafiles; 29 | for ( std::filesystem::recursive_directory_iterator end, dir( krb_files_dir ); 30 | dir != end; ++dir ) 31 | { 32 | auto path = dir->path(); 33 | if ( std::filesystem::is_regular_file( path ) ) 34 | { 35 | // find the file with metadata extension 36 | std::string filename = path.filename().string(); 37 | if ( !filename.empty() && filename.find( "_metadata" ) != std::string::npos ) 38 | { 39 | std::string filepath = path.parent_path().string() + "/" + filename; 40 | metadatafiles.push_back( filepath ); 41 | } 42 | } 43 | } 44 | 45 | // read the information of service account from the files 46 | for ( auto file_path : metadatafiles ) 47 | { 48 | std::list krb_ticket_info_list = 49 | read_meta_data_json( file_path ); 50 | std::string log_message; 51 | 52 | // refresh the kerberos tickets for the service accounts, if tickets ready for 53 | // renewal 54 | for ( auto krb_ticket : krb_ticket_info_list ) 55 | { 56 | std::pair gmsa_ticket_result; 57 | std::string krb_cc_name = krb_ticket->krb_file_path; 58 | std::string domainless_user = krb_ticket->domainless_user; 59 | // check if the ticket is ready for renewal and not created in domainless mode 60 | if ( ( domainless_user.empty() || 61 | domainless_user.find( "awsdomainlessusersecret" ) != 62 | std::string::npos ) && 63 | is_ticket_ready_for_renewal( krb_ticket, cf_daemon.cf_logger ) ) 64 | { 65 | int num_retries = 1; 66 | for ( int i = 0; i <= num_retries; i++ ) 67 | { 68 | gmsa_ticket_result = fetch_gmsa_password_and_create_krb_ticket( 69 | krb_ticket->domain_name, krb_ticket, krb_cc_name, cf_logger ); 70 | if ( gmsa_ticket_result.first != 0 ) 71 | { 72 | std::pair status; 73 | log_message = "ERROR: Cannot get gMSA krb ticket using account " + 74 | krb_ticket->service_account_name; 75 | cf_logger.logger( LOG_ERR, log_message.c_str() ); 76 | if ( domainless_user.find( "awsdomainlessusersecret" ) != 77 | std::string::npos ) 78 | { 79 | int pos = domainless_user.find( ":" ); 80 | std::string domainlessUser = domainless_user.substr( pos + 1 ); 81 | status = Util::generate_krb_ticket_using_secret_vault( 82 | krb_ticket->domain_name, domainlessUser, cf_logger ); 83 | } 84 | else 85 | { 86 | status = generate_krb_ticket_from_machine_keytab( 87 | krb_ticket->domain_name, cf_logger ); 88 | } 89 | if ( status.first < 0 ) 90 | { 91 | log_message = "Error " + std::to_string( status.first ) + 92 | ": Cannot get machine krb ticket"; 93 | cf_logger.logger( LOG_ERR, log_message.c_str() ); 94 | } 95 | else 96 | { 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | /* 103 | else 104 | { 105 | log_message = "gMSA ticket is at " + krb_cc_name; 106 | cf_logger.logger( LOG_INFO, log_message.c_str() ); 107 | } 108 | */ 109 | } 110 | } 111 | } 112 | catch ( const std::exception& ex ) 113 | { 114 | std::string log_str = Util::getCurrentTime() + '\t' + "ERROR: '" + ex.what() + "'!\n"; 115 | cf_logger.logger( LOG_ERR, log_str.c_str() ); 116 | std::cerr << log_str << std::endl; 117 | log_str = Util::getCurrentTime() + '\t' + "ERROR: failed to run ticket renewal"; 118 | std::cerr << log_str << std::endl; 119 | cf_logger.logger( LOG_ERR, log_str.c_str() ); 120 | break; 121 | } 122 | } 123 | return -1; 124 | } 125 | -------------------------------------------------------------------------------- /renewal/tests/renewal_test.cpp: -------------------------------------------------------------------------------- 1 | #include "daemon.h" 2 | #include 3 | 4 | int renewal_failure_krb_dir_not_found_test() 5 | { 6 | Daemon cf_daemon; 7 | 8 | int result = krb_ticket_renew_handler( cf_daemon ); 9 | 10 | if ( result != -1 ) 11 | { 12 | std::cout << "\nkrb dir not found test is failed" << std::endl; 13 | return EXIT_FAILURE; 14 | } 15 | 16 | std::cout << "\nkrb dir not found test is successful" << std::endl; 17 | return EXIT_SUCCESS; 18 | } 19 | -------------------------------------------------------------------------------- /sample/java-sample-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:11-oracle 2 | 3 | RUN microdnf install krb5-workstation unzip wget -y 4 | RUN microdnf clean all 5 | 6 | WORKDIR /app 7 | 8 | COPY mssql-jdbc-12.8.1.jre8.jar /app/ 9 | 10 | # Copy your Java file 11 | COPY SQLServerKerberosConnection.java /app/ 12 | 13 | # Compile with JDBC driver in classpath 14 | RUN javac -cp "/app/mssql-jdbc-12.8.1.jre8.jar" SQLServerKerberosConnection.java 15 | 16 | ENV LEASE_ID="default" 17 | 18 | ENV SERVER_NAME=default_server \ 19 | DOMAIN_NAME=default_domain \ 20 | USER_NAME=default_user 21 | 22 | # Run the Java program with parameters 23 | CMD ["sh", "-c", "export KRB5CCNAME=/var/credentials-fetcher/krbdir/${LEASE_ID}/WebApp01/krb5cc && echo \"Using KRB5CCNAME: $KRB5CCNAME\" && klist && java -cp '.:/app/mssql-jdbc-12.8.1.jre8.jar' SQLServerKerberosConnection ${SERVER_NAME} ${DOMAIN_NAME} ${USER_NAME}"] 24 | -------------------------------------------------------------------------------- /sample/java-sample-app/README.md: -------------------------------------------------------------------------------- 1 | # SQL Server Kerberos Authentication Java Application 2 | 3 | A simple Java application that connects to Microsoft SQL Server using Kerberos authentication and executes a simple Select command on the database. 4 | 5 | ## Prerequisites 6 | 7 | - Microsoft JDBC Driver for SQL Server (JDBC driver used to create this app is ```mssql-jdbc-12.8.1.jre8.jar```) 8 | - Valid Kerberos ticket (can be verified using `klist`) 9 | - SQL Server configured for Kerberos authentication 10 | 11 | ## Files Required 12 | 13 | 1. `SQLServerKerberosConnection.class` - The compiled Java class file 14 | 2. `mssql-jdbc-12.8.1.jre8.jar` - Microsoft JDBC driver for SQL Server 15 | 3. `Dockerfile ` - The Dockerfile in this directory 16 | 17 | ## Environment Setup 18 | 19 | 1. Ensure you have a valid Kerberos ticket: 20 | ``` 21 | klist 22 | ``` 23 | 24 | 2. Build the dockerfile 25 | ``` 26 | docker build -t myjava . 27 | ``` 28 | 29 | 3. Run the docker container 30 | ``` 31 | docker run -it \ 32 | -e LEASE_ID= \ 33 | -e SERVER_NAME= (example: EC2AMAZ-938NTBH.contoso.com) \ 34 | -e DOMAIN_NAME= (example: CONTOSO.COM) \ 35 | -e USER_NAME= (example: standarduser01) \ 36 | -v /var/credentials-fetcher/krbdir:/var/credentials-fetcher/krbdir \ 37 | -v /etc/krb5.conf:/etc/krb5.conf:ro \ 38 | myjava 39 | ``` 40 | 41 | 4. You should see the following output 42 | ``` 43 | Using KRB5CCNAME: /var/credentials-fetcher/krbdir/49ccb67e16ba17c4f14f/WebApp01/krb5cc 44 | Ticket cache: FILE:/var/credentials-fetcher/krbdir/49ccb67e16ba17c4f14f/WebApp01/krb5cc 45 | Default principal: WebApp01$@CONTOSO.COM 46 | 47 | Valid starting Expires Service principal 48 | 02/07/25 22:36:38 02/08/25 08:36:38 krbtgt/CONTOSO.COM@CONTOSO.COM 49 | renew until 02/14/25 22:36:37 50 | Connected successfully using Kerberos authentication. 51 | +---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ 52 | | EmpID | EmpName | Designation | Department | JoiningDate | 53 | +---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ 54 | | 1 | CHIN YEN | LAB ASSISTANT | LAB | 2022-03-05 03:57:09.967 | 55 | +---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ 56 | | 2 | MIKE PEARL | SENIOR ACCOUNTANT | ACCOUNTS | 2022-03-05 03:57:09.967 | 57 | +---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ 58 | | 3 | GREEN FIELD | ACCOUNTANT | ACCOUNTS | 2022-03-05 03:57:09.967 | 59 | +---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ 60 | | 4 | DEWANE PAUL | PROGRAMMER | IT | 2022-03-05 03:57:09.967 | 61 | +---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ 62 | | 5 | MATTS | SR. PROGRAMMER | IT | 2022-03-05 03:57:09.967 | 63 | +---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ 64 | | 6 | PLANK OTO | ACCOUNTANT | ACCOUNTS | 2022-03-05 03:57:09.967 | 65 | ``` -------------------------------------------------------------------------------- /sample/java-sample-app/SQLServerKerberosConnection.java: -------------------------------------------------------------------------------- 1 | import java.sql.*; 2 | 3 | public class SQLServerKerberosConnection { 4 | public static void main(String[] args) { 5 | 6 | System.setProperty("java.security.krb5.principal", args[2] + "@" + args[1]); 7 | 8 | String connectionUrl = "jdbc:sqlserver://" + args[0] + ":1433;" 9 | + "databaseName=EmployeesDB;" 10 | + "integratedSecurity=true;" 11 | + "authenticationScheme=JavaKerberos;" 12 | + "userName=" + args[2] + "@" + args[1] + ";" 13 | + "serverSpn=MSSQLSvc/" + args[0] + ":1433;" 14 | + "trustServerCertificate=true"; 15 | 16 | try { 17 | // Ensure the JDBC driver is loaded 18 | Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); 19 | 20 | // Establish the connection 21 | try (Connection connection = DriverManager.getConnection(connectionUrl)) { 22 | System.out.println("Connected successfully using Kerberos authentication."); 23 | 24 | // Perform a simple query 25 | try (Statement statement = connection.createStatement(); 26 | ResultSet resultSet = statement.executeQuery("SELECT * from EmployeesDB.dbo.EmployeesTable")) { 27 | 28 | ResultSetMetaData metaData = resultSet.getMetaData(); 29 | int columnCount = metaData.getColumnCount(); 30 | 31 | String[] columns = new String[columnCount]; 32 | for (int i = 0; i < columnCount; i++) { 33 | columns[i] = metaData.getColumnName(i + 1); 34 | } 35 | printRow(columns); 36 | 37 | // Display data rows 38 | while (resultSet.next()) { 39 | String[] row = new String[columnCount]; 40 | for (int i = 0; i < columnCount; i++) { 41 | row[i] = resultSet.getString(i + 1); 42 | } 43 | printRow(row); 44 | } 45 | } 46 | } 47 | } catch (ClassNotFoundException e) { 48 | System.err.println("Error loading JDBC driver: " + e.getMessage()); 49 | } catch (SQLException e) { 50 | System.err.println("Error connecting to the database: " + e.getMessage()); 51 | } 52 | } 53 | 54 | 55 | private static void printRow(String[] row) { 56 | System.out.println("+---------------------------".repeat(row.length) + "+"); 57 | for (String col : row) { 58 | System.out.printf("| %-25s ", col != null ? col : "NULL"); 59 | } 60 | System.out.println("|"); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /scripts/systemd/credentials-fetcher.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=credentials-fetcher systemd service unit file. 3 | 4 | [Service] 5 | StandardError=journal 6 | StandardOutput=journal 7 | StandardInput=null 8 | ExecStartPre=mkdir -p /var/credentials-fetcher/krbdir /var/credentials-fetcher/socket /var/credentials-fetcher/logging 9 | ExecStartPre=chgrp ec2-user /var/credentials-fetcher /var/credentials-fetcher/krbdir /var/credentials-fetcher/socket /var/credentials-fetcher/logging 10 | ExecStartPre=chmod 755 /var/credentials-fetcher /var/credentials-fetcher/krbdir /var/credentials-fetcher/socket /var/credentials-fetcher/logging 11 | ExecStart=/usr/sbin/credentials-fetcherd 12 | ExecStartPost=chgrp ec2-user /var/credentials-fetcher/socket/credentials_fetcher.sock 13 | ExecStartPost=chmod 660 /var/credentials-fetcher/socket/credentials_fetcher.sock 14 | Environment="CREDENTIALS_FETCHERD_STARTED_BY_SYSTEMD=1" 15 | Type=notify 16 | NotifyAccess=main 17 | WatchdogSec=120s 18 | Restart=on-failure 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /setup-scripts/README.md: -------------------------------------------------------------------------------- 1 | ## Setup Scripts 2 | Setup scripts are meant to compile and build credentials-fetcher along with 3 | all its dependencies. These can be used to setup a development environment 4 | to build and test changes. You can choose to either setup a docker container 5 | or setup the dependencies on the instance itself. -------------------------------------------------------------------------------- /setup-scripts/docker-scripts/Dockerfile-ubuntu-20.04: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/ubuntu/ubuntu:20.04 as base 2 | ARG TIME_ZONE 3 | RUN if [ -z "${TIME_ZONE}" ] ; then exit 1; fi 4 | 5 | RUN apt-get update \ 6 | && DEBIAN_FRONTEND="noninteractive" TZ="${TIME_ZONE}" \ 7 | apt install -y git clang wget curl autoconf \ 8 | libglib2.0-dev libboost-dev libkrb5-dev libsystemd-dev libssl-dev \ 9 | libboost-program-options-dev libboost-filesystem-dev byacc make libjsoncpp-dev \ 10 | clang-12 libgtest-dev 11 | 12 | RUN ln -sf /usr/lib/llvm-12/bin/clang /usr/bin/clang \ 13 | && ln -sf /usr/lib/llvm-12/bin/clang++ /usr/bin/clang++ 14 | 15 | RUN git clone https://github.com/Kitware/CMake.git -b release /root/CMake \ 16 | && cd /root/CMake && ./configure && make -j4 && pwd && make install 17 | 18 | RUN git clone https://github.com/krb5/krb5.git -b krb5-1.21.2-final /root/krb5 \ 19 | && cd /root/krb5/src && autoconf && autoreconf && ./configure && make -j4 && make install 20 | 21 | RUN git clone --recurse-submodules -b v1.58.0 https://github.com/grpc/grpc /root/grpc \ 22 | && mkdir -p /root/grpc/build && cd /root/grpc/build \ 23 | && cmake -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DCMAKE_CXX_STANDARD=17 ../ \ 24 | && make -j4 && make install \ 25 | && mkdir -p /root/grpc/cmake/build && cd /root/grpc/cmake/build \ 26 | && cmake -DgRPC_BUILD_TESTS=ON ../.. && make grpc_cli \ 27 | && cp /root/grpc/cmake/build/grpc_cli /usr/local/bin 28 | 29 | RUN cd /root && wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \ 30 | && dpkg -i packages-microsoft-prod.deb \ 31 | && rm packages-microsoft-prod.deb \ 32 | && apt-get update \ 33 | && apt-get install -y dotnet-sdk-8.0 \ 34 | && ln -s '/usr/share/dotnet' '/usr/lib/dotnet' 35 | 36 | RUN git clone https://github.com/aws/credentials-fetcher /root/credentials-fetcher \ 37 | && mkdir -p /root/credentials-fetcher/build \ 38 | && mkdir -p /usr/lib64/glib-2.0/ \ 39 | && ln -s '/usr/lib/x86_64-linux-gnu/glib-2.0/include/' '/usr/lib64/glib-2.0/include' \ 40 | && ln -s '/usr/include/jsoncpp/json/' '/usr/include/json' \ 41 | && cd /root/credentials-fetcher/build \ 42 | && cmake ../ && make -j4 && make install 43 | 44 | WORKDIR /root/credentials-fetcher/build 45 | 46 | ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib 47 | 48 | CMD ["/bin/bash"] 49 | -------------------------------------------------------------------------------- /setup-scripts/docker-scripts/Dockerfile-ubuntu-22.04: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/ubuntu/ubuntu:22.04 as base 2 | ARG TIME_ZONE 3 | RUN if [ -z "${TIME_ZONE}" ] ; then exit 1; fi 4 | 5 | RUN apt-get update \ 6 | && DEBIAN_FRONTEND="noninteractive" TZ="${TIME_ZONE}" \ 7 | apt install -y git clang wget curl autoconf \ 8 | libglib2.0-dev libboost-dev libkrb5-dev libsystemd-dev libssl-dev \ 9 | libboost-program-options-dev libboost-filesystem-dev byacc make libjsoncpp-dev \ 10 | libgtest-dev 11 | 12 | RUN git clone https://github.com/Kitware/CMake.git -b release /root/CMake \ 13 | && cd /root/CMake && ./configure && make -j4 && pwd && make install 14 | 15 | RUN git clone https://github.com/krb5/krb5.git -b krb5-1.21.2-final /root/krb5 \ 16 | && cd /root/krb5/src && autoconf && autoreconf && ./configure && make -j4 && make install 17 | 18 | RUN git clone --recurse-submodules -b v1.58.0 https://github.com/grpc/grpc /root/grpc \ 19 | && mkdir -p /root/grpc/build && cd /root/grpc/build \ 20 | && cmake -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DCMAKE_CXX_STANDARD=17 ../ \ 21 | && make -j4 && make install \ 22 | && mkdir -p /root/grpc/cmake/build && cd /root/grpc/cmake/build \ 23 | && cmake -DgRPC_BUILD_TESTS=ON ../.. && make grpc_cli \ 24 | && cp /root/grpc/cmake/build/grpc_cli /usr/local/bin 25 | 26 | RUN cd /root && wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \ 27 | && dpkg -i packages-microsoft-prod.deb \ 28 | && rm packages-microsoft-prod.deb \ 29 | && apt remove 'dotnet*' 'aspnetcore*' 'netstandard*' \ 30 | && rm /etc/apt/sources.list.d/microsoft-prod.list \ 31 | && apt update \ 32 | && apt-get install -y dotnet-sdk-8.0 33 | 34 | #RUN git clone -b credentials-fetcher-credfile https://github.com/fordth/credentials-fetcher /root/credentials-fetcher \ 35 | RUN git clone https://github.com/aws/credentials-fetcher /root/credentials-fetcher \ 36 | && mkdir -p /root/credentials-fetcher/build \ 37 | && mkdir -p /usr/lib64/glib-2.0/ \ 38 | && ln -s '/usr/lib/x86_64-linux-gnu/glib-2.0/include/' '/usr/lib64/glib-2.0/include' \ 39 | && ln -s '/usr/include/jsoncpp/json/' '/usr/include/json' \ 40 | && cd /root/credentials-fetcher/build \ 41 | && cmake ../ && make -j4 && make install 42 | 43 | WORKDIR /root/credentials-fetcher/build 44 | 45 | ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib 46 | 47 | CMD ["/bin/bash"] 48 | -------------------------------------------------------------------------------- /setup-scripts/shell-scripts/ubuntu-22.04-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run as root 4 | if [ "$EUID" -ne 0 ] 5 | then echo "Please run as root" 6 | exit 7 | fi 8 | 9 | # Set timezone 10 | TIME_ZONE="UTC" 11 | 12 | 13 | USER_DIR="/home/ubuntu" # Default directory 14 | 15 | echo "Do you want to use a different directory instead of /home/ubuntu? (y/n)" 16 | read response 17 | 18 | if [[ $response =~ ^[Yy]$ ]]; then 19 | echo "Please enter the directory path:" 20 | read user_input 21 | 22 | if [ -d "$user_input" ]; then 23 | USER_DIR="$user_input" 24 | else 25 | echo "Warning: The directory $user_input does not exist. Using default: $USER_DIR" 26 | fi 27 | fi 28 | 29 | cd "$USER_DIR" 30 | 31 | echo "Installing dependencies for credentials-fetcher" 32 | apt-get update \ 33 | && DEBIAN_FRONTEND="noninteractive" TZ="${TIME_ZONE}" \ 34 | apt install -y git clang wget curl autoconf \ 35 | libglib2.0-dev libboost-dev libkrb5-dev libsystemd-dev libssl-dev \ 36 | libboost-program-options-dev libboost-filesystem-dev byacc make \ 37 | libjsoncpp-dev libgtest-dev pip python3.10-venv \ 38 | libsasl2-modules-gssapi-mit:amd64 ldap-utils krb5-config awscli 39 | 40 | 41 | git clone https://github.com/Kitware/CMake.git -b release \ 42 | && cd CMake && ./configure && make -j4 && pwd && make install 43 | 44 | if [ $? -ne 0 ]; then 45 | echo "error: Cmake installation failed" 46 | exit 1 47 | else 48 | echo "CMake successfully installed, now installing krb5" 49 | fi 50 | 51 | cd "$USER_DIR" 52 | 53 | 54 | git clone https://github.com/krb5/krb5.git -b krb5-1.21.2-final \ 55 | && cd krb5/src && autoconf && autoreconf && ./configure && make -j4 && make install 56 | 57 | if [ $? -ne 0 ]; then 58 | echo "error: krb5 installation failed" 59 | exit 1 60 | else 61 | echo "krb5 successfully installed, now installing grpc" 62 | fi 63 | 64 | cd "$USER_DIR" 65 | 66 | git clone --recurse-submodules -b v1.58.0 https://github.com/grpc/grpc && mkdir -p grpc/build && cd grpc/build && cmake -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DCMAKE_CXX_STANDARD=17 ../ && make -j4 && make install 67 | 68 | cd "$USER_DIR" 69 | 70 | mkdir -p grpc/cmake/build && cd grpc/cmake/build \ 71 | && cmake -DgRPC_BUILD_TESTS=ON ../.. && make grpc_cli \ 72 | && cp grpc_cli /usr/local/bin 73 | 74 | if [ $? -ne 0 ]; then 75 | echo "error: grpc installation failed" 76 | exit 1 77 | else 78 | echo "grpc successfully installed, now installing Microsoft packages" 79 | fi 80 | 81 | cd "$USER_DIR" 82 | 83 | wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \ 84 | && DEBIAN_FRONTEND=noninteractive dpkg -i packages-microsoft-prod.deb \ 85 | && rm packages-microsoft-prod.deb \ 86 | && apt-get remove -y 'dotnet*' 'aspnetcore*' 'netstandard*' \ 87 | && rm /etc/apt/sources.list.d/microsoft-prod.list \ 88 | && apt-get update -y \ 89 | && apt-get install -y dotnet-sdk-8.0 90 | 91 | mkdir -p /usr/lib64/glib-2.0/ && ln -s '/usr/lib/x86_64-linux-gnu/glib-2.0/include/' '/usr/lib64/glib-2.0/include' && ln -s '/usr/include/jsoncpp/json/' '/usr/include/json' 92 | 93 | mkdir -p /var/credentials-fetcher/logging 94 | mkdir -p /var/credentials-fetcher/socket 95 | mkdir -p /var/credentials-fetcher/krbdir 96 | 97 | if [ $? -ne 0 ]; then 98 | echo "error: Microsoft packages installation failed" 99 | exit 1 100 | else 101 | echo "Microsoft packages successfully installed. Please follow the instructions in the setup doc to clone the repo and build it" 102 | fi 103 | 104 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib 105 | 106 | cd "$USER_DIR" 107 | git clone -b dev https://github.com/aws/credentials-fetcher.git # update branch as needed 108 | mkdir -p credentials-fetcher/build 109 | cd credentials-fetcher/build 110 | cmake ../ && make -j4 && make install 111 | 112 | -------------------------------------------------------------------------------- /suppressions.txt: -------------------------------------------------------------------------------- 1 | *:CONTRIBUTING.md 2 | *:README.md 3 | *:NOTICE 4 | *:LICENSE 5 | missingIncludeSystem:* 6 | missingInclude:* 7 | unmatchedSuppression:* 8 | ConfigurationNotChecked:* 9 | cstyleCast:daemon/src/daemon.cpp 10 | passedByValue:* 11 | useStlAlgorithm:auth/kerberos/src/krb.cpp 12 | useInitializationList:api/src/gmsa_service.cpp -------------------------------------------------------------------------------- /test/tester.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "daemon.h" 4 | 5 | TEST(DaemonTest, InvalidCharacterTest) { 6 | ASSERT_EQ(contains_invalid_characters("abcdef"), 0); 7 | } 8 | --------------------------------------------------------------------------------