Showing preview only (306K chars total). Download the full file or copy to clipboard to get everything.
Repository: DaspawnW/vault-crd
Branch: master
Commit: 9037143a6b0c
Files: 110
Total size: 274.3 KB
Directory structure:
gitextract_p7tbtk3k/
├── .github/
│ └── workflows/
│ ├── codeql-analysis.yml
│ └── maven.yaml
├── .gitignore
├── .mvn/
│ └── wrapper/
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── Dockerfile
├── LICENSE
├── crd.yml
├── deploy/
│ ├── admission-webhook.yaml
│ └── rbac.yaml
├── examples/
│ ├── cert.yml
│ ├── certjks-rollout-redo.yml
│ ├── certjks.yml
│ ├── dockercfg-error.yml
│ ├── dockercfg.yml
│ ├── keyvalue.yml
│ ├── keyvaluev2-version.yml
│ ├── keyvaluev2.yml
│ ├── kind/
│ │ ├── cluster.yaml
│ │ ├── run.sh
│ │ └── vault.yaml
│ ├── pki.yml
│ ├── pki_chain.yml
│ ├── pkijks.yml
│ └── properties.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── readme.md
└── src/
├── main/
│ ├── java/
│ │ └── de/
│ │ └── koudingspawn/
│ │ └── vault/
│ │ ├── Constants.java
│ │ ├── VaultApplication.java
│ │ ├── admissionreview/
│ │ │ ├── AdmissionReviewRestService.java
│ │ │ └── AdmissionReviewService.java
│ │ ├── crd/
│ │ │ ├── Vault.java
│ │ │ ├── VaultChangeAdjustmentCallback.java
│ │ │ ├── VaultDockerCfgConfiguration.java
│ │ │ ├── VaultJKSConfiguration.java
│ │ │ ├── VaultList.java
│ │ │ ├── VaultPkiConfiguration.java
│ │ │ ├── VaultPropertiesConfiguration.java
│ │ │ ├── VaultSpec.java
│ │ │ ├── VaultType.java
│ │ │ └── VaultVersionedConfiguration.java
│ │ ├── kubernetes/
│ │ │ ├── ChangeAdjustmentService.java
│ │ │ ├── EventHandler.java
│ │ │ ├── KubernetesConnection.java
│ │ │ ├── KubernetesService.java
│ │ │ ├── Watcher.java
│ │ │ ├── cache/
│ │ │ │ ├── SecretCache.java
│ │ │ │ └── SecretCacheConfiguration.java
│ │ │ ├── event/
│ │ │ │ ├── EventNotification.java
│ │ │ │ └── EventType.java
│ │ │ └── scheduler/
│ │ │ ├── RefreshConfiguration.java
│ │ │ ├── RequiresRefresh.java
│ │ │ ├── ScheduledRefresh.java
│ │ │ ├── TypeRefreshFactory.java
│ │ │ └── impl/
│ │ │ ├── CertJksRefresh.java
│ │ │ ├── CertRefresh.java
│ │ │ ├── CompareHash.java
│ │ │ ├── DockerCfgRefresh.java
│ │ │ ├── KeyValueRefresh.java
│ │ │ ├── KeyValueV2Refresh.java
│ │ │ ├── PkiJksRefresh.java
│ │ │ ├── PkiRefresh.java
│ │ │ └── PropertiesRefresh.java
│ │ └── vault/
│ │ ├── TypedSecretGenerator.java
│ │ ├── TypedSecretGeneratorFactory.java
│ │ ├── VaultCommunication.java
│ │ ├── VaultConfiguration.java
│ │ ├── VaultHealthCheck.java
│ │ ├── VaultSecret.java
│ │ ├── VaultService.java
│ │ ├── communication/
│ │ │ ├── SecretNotAccessibleException.java
│ │ │ ├── TokenLookup.java
│ │ │ └── TokenLookupData.java
│ │ └── impl/
│ │ ├── CertGenerator.java
│ │ ├── CertJksGenerator.java
│ │ ├── DockerCfgGenerator.java
│ │ ├── EncryptionUtils.java
│ │ ├── KeyValueGenerator.java
│ │ ├── KeyValueV2Generator.java
│ │ ├── PkiJksGenerator.java
│ │ ├── PkiSecretGenerator.java
│ │ ├── PropertiesGenerator.java
│ │ ├── Sha256.java
│ │ ├── SharedVaultResponseMapper.java
│ │ ├── dockercfg/
│ │ │ └── PullSecret.java
│ │ ├── pki/
│ │ │ ├── PKIRequest.java
│ │ │ ├── PKIResponse.java
│ │ │ └── VaultResponseData.java
│ │ └── properties/
│ │ └── VaultJinjaLookup.java
│ └── resources/
│ └── application.properties
└── test/
├── java/
│ └── de/
│ └── koudingspawn/
│ └── vault/
│ ├── CertChainTest.java
│ ├── CertTest.java
│ ├── DockerCfgTest.java
│ ├── EventNotificationTest.java
│ ├── KeyValueTest.java
│ ├── KeyValueV2Test.java
│ ├── OwnerReferenceBugfixTest.java
│ ├── PKIChainTest.java
│ ├── PKITest.java
│ ├── PropertiesTest.java
│ ├── TestHelper.java
│ ├── admissionreview/
│ │ └── AdmissionReviewTest.java
│ ├── kubernetes/
│ │ ├── EventHandlerTest.java
│ │ └── KubernetesServiceTest.java
│ └── vault/
│ └── VaultHealthCheckTest.java
└── resources/
├── application.properties
├── test.properties
└── vault-crd.yaml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '26 9 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17
cache: 'maven'
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- run: mvn -B package --file pom.xml -Dspring.profiles.active=test -DskipTests
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
================================================
FILE: .github/workflows/maven.yaml
================================================
name: Java CI
on: [push]
jobs:
test:
name: "Test"
runs-on: ubuntu-latest
strategy:
matrix:
kubernetes_version:
- "kindest/node:v1.29.1@sha256:a0cc28af37cf39b019e2b448c54d1a3f789de32536cb5a5db61a49623e527144"
- "kindest/node:v1.28.6@sha256:b7e1cf6b2b729f604133c667a6be8aab6f4dde5bb042c1891ae248d9154f665b"
- "kindest/node:v1.27.10@sha256:3700c811144e24a6c6181065265f69b9bf0b437c45741017182d7c82b908918f"
- "kindest/node:v1.26.13@sha256:15ae92d507b7d4aec6e8920d358fc63d3b980493db191d7327541fbaaed1f789"
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17
cache: 'maven'
- uses: helm/kind-action@v1.5.0
with:
version: "v0.17.0"
node_image: "${{ matrix.kubernetes_version }}"
- name: "Kubernetes version"
run: |
kubectl version
- name: "Create Custom Resource"
run: |
kubectl apply -f crd.yml
- name: Build with Maven
run: mvn -B package --file pom.xml -Dspring.profiles.active=test
docker-push:
name: Docker Push (GHCR & public ECR)
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/docker-')
permissions:
id-token: write
contents: read
packages: write
needs: test
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set output
id: vars
run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
- name: Docker publish
uses: daspawnw/docker-multi-build-push-action@master
with:
platforms: "linux/amd64,linux/arm64"
docker-tag: "${{ steps.vars.outputs.tag }}"
ghcr-enabled: "true"
ghcr-token: "${{ secrets.GITHUB_TOKEN }}"
ecr-enabled: ${{ github.repository == 'daspawnw/vault-crd' }}
ecr-role-to-assume: "${{ secrets.AWS_PUBLIC_ECR_ARN }}"
ecr-repository-url: "public.ecr.aws/l2l6k4u5/vault-crd"
================================================
FILE: .gitignore
================================================
target/*
.idea/*
*.iml
vault/*
================================================
FILE: .mvn/wrapper/maven-wrapper.properties
================================================
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip
================================================
FILE: Dockerfile
================================================
FROM gcr.io/distroless/java17-debian11:nonroot AS SECURITY
FROM openjdk:17 AS BUILD
COPY . /opt
WORKDIR /opt
RUN ./mvnw clean install -DskipTests
ENV JAVA_RANDOM="file:/dev/./urandom"
COPY --from=SECURITY /etc/java-17-openjdk/security/java.security /java.security
RUN echo "networkaddress.cache.ttl=60" >> /java.security
RUN sed -i -e "s@^securerandom.source=.*@securerandom.source=${JAVA_RANDOM}@" /java.security
FROM gcr.io/distroless/java17-debian11:nonroot
COPY --from=BUILD /opt/target/vault-crd.jar /opt/vault-crd.jar
COPY --from=BUILD /java.security /etc/java-17-openjdk/security/java.security
ENTRYPOINT ["/usr/bin/java", "-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts", "-Djavax.net.ssl.trustStorePassword=changeit", "-Djavax.net.ssl.trustStoreType=jks", "-Dkeystore.pkcs12.legacy"]
CMD ["-jar", "/opt/vault-crd.jar"]
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" files file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE files file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE files from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The files should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: crd.yml
================================================
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: vault.koudingspawn.de
spec:
group: koudingspawn.de
scope: Namespaced
names:
plural: vault
singular: vault
kind: Vault
shortNames:
- vt
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
path:
type: string
pattern: '^.*?\/.*?(\/.*?)?$'
type:
type: string
enum:
- PKI
- PKIJKS
- CERT
- CERTJKS
- DOCKERCFG
- KEYVALUE
- KEYVALUEV2
- PROPERTIES
pkiConfiguration:
type: object
properties:
commonName:
type: string
altNames:
type: string
ipSans:
type: string
ttl:
type: string
pattern: '^[0-9]{1,}[hm]$'
jksConfiguration:
type: object
properties:
password:
type: string
alias:
type: string
keyName:
type: string
caAlias:
type: string
versionConfiguration:
type: object
properties:
version:
type: integer
propertiesConfiguration:
type: object
properties:
context:
type: object
x-kubernetes-preserve-unknown-fields: true
files:
type: object
x-kubernetes-preserve-unknown-fields: true
dockerCfgConfiguration:
type: object
properties:
type:
type: string
enum:
- KEYVALUE
- KEYVALUEV2
version:
type: integer
changeAdjustmentCallback:
type: object
properties:
type:
type: string
name:
type: string
required:
- type
================================================
FILE: deploy/admission-webhook.yaml
================================================
apiVersion: v1
kind: Service
metadata:
name: vault-crd
namespace: vault-crd
spec:
selector:
app: vault-crd
ports:
- port: 8080
type: ClusterIP
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
labels:
app: vault-crd
name: vault-crd-admission
webhooks:
- name: validate.vault.koudingspawn.de
admissionReviewVersions: ["v1"]
sideEffects: None
rules:
- apiGroups:
- koudingspawn.de
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- vault
failurePolicy: Fail
clientConfig:
service:
namespace: vault-crd
name: vault-crd
path: /validation/vault-crd
port: 8080
caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRVENDQWltZ0F3SUJBZ0lVUDk4cG9lNXF0TVExWXhrVS85eHc5ZGp4N05Bd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0dqRVlNQllHQTFVRUF4TVBhMjkxWkdsdVozTndZWGR1TG1SbE1CNFhEVEl3TURZeU5ERTVNVEF4TkZvWApEVE13TURZeU1qRTVNVEEwTkZvd0dqRVlNQllHQTFVRUF4TVBhMjkxWkdsdVozTndZWGR1TG1SbE1JSUJJakFOCkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXNWbklIY2JCeHl2NmpOS3V5YkZpMFNkbEtONlAKU1U2RlJOUzJwQ1NNK3R3eWtCblllZUtacTMxQmZYQW5EMlVrR1dod0gwWHF1QmJ4S3Bsa2lYWVVMT25qTFQwLwpWVG5LN1U2NWFWZ0VMZlZJVFNHRnJFcjMwdTdYbWN5NkNWZmkzd25CMjZkZnR1V2NSWlFoaXIxZUE4VUZjejBQCkxlTWhiSTRybENWcnU2bHFFTzl0bGRId21ScGc5dWYyYnJiTi9PNDlaSUtYVGRBSW5jVTVacnV3d21MOVpnbUIKc2tzaDBvZWFkaXpMbzRuSmNjdVZYQjlwMHJjcjBPdG5qSHo1SGdCUElzTDB6UVZMUzVSZ01CTkxDbTVnZjlrcAp4S1UzQ0ZnU0Z0Vng5WlE3aG9hUEl3VnRqWUNrcCtzQVBycjBQNkk0Um95c2U3RDB2UERQUCs5NDF3SURBUUFCCm8zOHdmVEFPQmdOVkhROEJBZjhFQkFNQ0FRWXdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QWRCZ05WSFE0RUZnUVUKa0hrOXBHMkg0eTJac0ZEUVZlNGJVTGhoMCtrd0h3WURWUjBqQkJnd0ZvQVVrSGs5cEcySDR5MlpzRkRRVmU0YgpVTGhoMCtrd0dnWURWUjBSQkJNd0VZSVBhMjkxWkdsdVozTndZWGR1TG1SbE1BMEdDU3FHU0liM0RRRUJDd1VBCkE0SUJBUUFoMW9pMy9iRW5Lb1Rxdkt2NEZZeVJKVDQzMkIxYkp2MG94ZERLaFJndVowYmQ1WXVOMHBnNTcxL2QKb1UvVUN6ellzaUVYZmw3NHREUndNWVUveXhlQVJKQ3B2RWswcVhOdHJlS1hZL0dDU0wxbjlKU1dTMk1xVDBBeQpuTWxqdEkrd3R5Ujh2MW05SnppNFdFNHdWdVRuclhlb1BSeVpKR0F1Q2xRbnk2VW5Fei9LS3ZvQ0pCQW1UY1NKCk5hZkNwYUh3b25sQVp5bXZRN0JQZHZoMk52ckdQazk2aEVZc1lnUVl6VW5KcURoT0Z2RWF1MjRLeEk2NlpXUnIKMGlSNkZmTTQ2ZjBNRVdqdmRSUGRucHh2dDhPbTBiNjVER0czc2hVTHNLZWJENFI4YjdCeTdrVUV0U1FkNVkzcwpHK1k5RTdpbXdWR1hlUTh1eWw3ZGNSV1AwTkJ1Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
================================================
FILE: deploy/rbac.yaml
================================================
apiVersion: v1
kind: Namespace
metadata:
name: vault-crd
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-crd-serviceaccount
namespace: vault-crd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: vault-crd-clusterrole
rules:
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- apiGroups:
- "koudingspawn.de"
resources:
- vault
verbs:
- list
- watch
- get
- apiGroups:
- ""
resources:
- secrets
- events
verbs:
- get
- create
- patch
- update
- delete
- watch
- list
- apiGroups:
- extensions
- apps
resources:
- deployments
verbs:
- update
- get
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: vault-crd-clusterrole-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: vault-crd-clusterrole
subjects:
- kind: ServiceAccount
name: vault-crd-serviceaccount
namespace: vault-crd
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: vault.koudingspawn.de
spec:
group: koudingspawn.de
scope: Namespaced
names:
plural: vault
singular: vault
kind: Vault
shortNames:
- vt
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
path:
type: string
pattern: '^.*?\/.*?(\/.*?)?$'
type:
type: string
enum:
- PKI
- PKIJKS
- CERT
- CERTJKS
- DOCKERCFG
- KEYVALUE
- KEYVALUEV2
- PROPERTIES
pkiConfiguration:
type: object
properties:
commonName:
type: string
altNames:
type: string
ipSans:
type: string
ttl:
type: string
pattern: '^[0-9]{1,}[hm]$'
jksConfiguration:
type: object
properties:
password:
type: string
alias:
type: string
keyName:
type: string
caAlias:
type: string
versionConfiguration:
type: object
properties:
version:
type: integer
propertiesConfiguration:
type: object
properties:
context:
type: object
x-kubernetes-preserve-unknown-fields: true
files:
type: object
x-kubernetes-preserve-unknown-fields: true
dockerCfgConfiguration:
type: object
properties:
type:
type: string
enum:
- KEYVALUE
- KEYVALUEV2
version:
type: integer
changeAdjustmentCallback:
type: object
properties:
type:
type: string
name:
type: string
required:
- type
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: vault-crd
name: vault-crd
namespace: vault-crd
spec:
selector:
matchLabels:
app: vault-crd
replicas: 1
template:
metadata:
labels:
app: vault-crd
spec:
serviceAccountName: vault-crd-serviceaccount
# initContainers:
# - name: convert-https
# image: shamelesscookie/openssl:1.1.1g
# command:
# - /bin/bash
# args:
# - "-c"
# - "openssl pkcs12 -export -in /opt/certificate/tls.crt -inkey /opt/certificate/tls.key -out /opt/target/keystore.p12 -passout pass:changeit -name admission-tls"
# volumeMounts:
# - mountPath: /opt/certificate
# name: pem-cert
# - mountPath: /opt/target
# name: pkcs12-cert
containers:
- name: vault-crd
image: daspawnw/vault-crd:1.11.0
env:
- name: KUBERNETES_VAULT_URL
value: "http://localhost:8080/v1/"
- name: KUBERNETES_VAULT_TOKEN
valueFrom:
secretKeyRef:
name: vault-token
key: token
# - name: SERVER_SSL_KEY-STORE-TYPE
# value: PKCS12
# - name: SERVER_SSL_KEY-STORE
# value: "/opt/certificate/keystore.p12"
# - name: SERVER_SSL_KEY-STORE-PASSWORD
# value: changeit
# - name: SERVER_SSL_KEY-ALIAS
# value: "admission-tls"
ports:
- containerPort: 8080
livenessProbe:
httpGet:
port: 8080
path: "/actuator/health"
# scheme: HTTPS
initialDelaySeconds: 30
failureThreshold: 3
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
# volumeMounts:
# - mountPath: /opt/certificate
# name: pkcs12-cert
# volumes:
# - name: pem-cert
# secret:
# secretName: vault-crd-tls
# - name: pkcs12-cert
# emptyDir: {}
restartPolicy: Always
---
apiVersion: v1
kind: Secret
metadata:
name: vault-token
namespace: vault-crd
data:
token: "cm9vdA=="
---
#apiVersion: v1
#kind: Secret
#metadata:
# name: vault-crd-tls
# namespace: vault-crd
#data:
# tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURjakNDQWxxZ0F3SUJBZ0lVY3d6Z0hrdjRhOXpHOE5hQm1rbVFPdGxZM3hjd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0dqRVlNQllHQTFVRUF4TVBhMjkxWkdsdVozTndZWGR1TG1SbE1CNFhEVEl3TURZeU5ERTVNVEExTUZvWApEVEl3TURjeU5qRTVNVEV5TUZvd0hqRWNNQm9HQTFVRUF4TVRkbUYxYkhRdFkzSmtMblpoZFd4MExXTnlaRENDCkFTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTG84Z2lWMjQ5UTArazMvenFnY2xVN3oKTisyT2N1VDNERG5JbXZaYTNiOXZGYjRXNVhOaXlpT0xtbHhnMDB3RGkyV01DcEI1RldmRUQwQU5KQUpWMEdRdwowVmlFTG5TVDRJYTRUcTlxUWlvc0J0RXd5Vkx1QkZSU1RHTDJiSUV5K3dBaGlrQ3dmbEI2L2trN05VbHlXZG8rCkRtcUorbDQ4RnVkbytpdTJyYkxtR0lnMTFDTy8rUTJlRnZCaTBaTTZEUzliUVNYOUYxTmMxdFZyNndtSWNOTHQKNzRHT2JQaG5rbFBaQjMrUzRFTk8xbHhzcCtkNGw0QkZEYWJWMHdCdWVmaFdqdktMSlZNRzlOWWZkWjdBaXArZwpYWWhpYVpPQS9xTlhSTDQzWlY1OXBBS2NtNWlFdUFLMStrYVBuWUdYQUtRRFE3L29hSlJwbEVqT1VLVHRObDBDCkF3RUFBYU9CcXpDQnFEQU9CZ05WSFE4QkFmOEVCQU1DQTZnd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUIwR0ExVWREZ1FXQkJRNjFvN2cwVllHc2pZZTJ2bkVKQ29LTElvYnREQWZCZ05WSFNNRQpHREFXZ0JTUWVUMmtiWWZqTFptd1VOQlY3aHRRdUdIVDZUQTNCZ05WSFJFRU1EQXVnaE4yWVhWc2RDMWpjbVF1CmRtRjFiSFF0WTNKa2doZDJZWFZzZEMxamNtUXVkbUYxYkhRdFkzSmtMbk4yWXpBTkJna3Foa2lHOXcwQkFRc0YKQUFPQ0FRRUFSVTVud05rb24xRWdxZWVQNnRHZzZiNmY5TC94cnd5bHFndURyWVJOUmIyeGl1ZTV0UDREZFNVZAo2eXBVNXdsWGdHTmlmcHBpeVhSbFlIZUtIT09jRUtlTlByb25KYnBaa25Wb3ZpaVU2aWhJNFBLM3lRRW51N2twCjhjdldxVDh2WVVJa2VwV0dEd2NYRTRNNWlSelJ5VXVYSkxXZk4yRnJjTlJ1RUdEL3JKRFlwQ00rTDNDd0U5TFgKbmFCaDRJTG1HZjJmMHpZaVZMWTN0bTF4c1E2ZGZCQlZMMHZDZmlBVzlKYkNGbTVSOXRySWttcXdBY09lQTFGUApMdExUNjJ5OHRZbjFFK0h6ZTVneVRBdXpoRHhHbmxXRGtkRDJVSXMrbUxqcWxOVzZrcTI0NHFJOUdZZ3drVFJiCm1DOURyZ2lFRWFIQi9yN2lBZVNaWHZkUzg2ckVpZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
# tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdWp5Q0pYYmoxRFQ2VGYvT3FCeVZUdk0zN1k1eTVQY01PY2lhOWxyZHYyOFZ2aGJsCmMyTEtJNHVhWEdEVFRBT0xaWXdLa0hrVlo4UVBRQTBrQWxYUVpERFJXSVF1ZEpQZ2hyaE9yMnBDS2l3RzBUREoKVXU0RVZGSk1ZdlpzZ1RMN0FDR0tRTEIrVUhyK1NUczFTWEpaMmo0T2FvbjZYandXNTJqNks3YXRzdVlZaURYVQpJNy81RFo0VzhHTFJrem9OTDF0QkpmMFhVMXpXMVd2ckNZaHcwdTN2Z1k1cytHZVNVOWtIZjVMZ1EwN1dYR3luCjUzaVhnRVVOcHRYVEFHNTUrRmFPOG9zbFV3YjAxaDkxbnNDS242QmRpR0pwazREK28xZEV2amRsWG4ya0FweWIKbUlTNEFyWDZSbytkZ1pjQXBBTkR2K2hvbEdtVVNNNVFwTzAyWFFJREFRQUJBb0lCQUh5TjhXRUxGYTZjUy9lVQpxV3NIeXRnRmxKY2RtVHdHK2pjL01seW5RdjFBVnlOTi91RmY1ZDlHQTlQYXNoWjVuR1lxOWZuUDhYLzN3VmRPCk1wSVpRSWx4bU9HQmJleHI1bE5UdXRSWTFhMk15blpvRVkyVVFITUFvN1BnS1l0elJDbS9STTZrKzZYcHpGMi8KNnBDWG1QNThXSG5xay9jb2F3MFR5WlVvMVJ6NjMvemxSYXJBMXdqQVVGMDNJRElNTFlzYWxCZld5Nm9neEhHZQpuOC95Z1E4cyt5Z29UY3FZb1hHYnZ2a01IOVB6b1oyZko3RWlrTW9Cb29jcjRxbjZZYUFIb3BJR0dMYmM5MFdsCmZVUHBheEtkcDMzUTlXK1MzbU11bm9zUk9MbHdqMS9HTHhaKzJWRkZqUTJseHpBOGdqOW1SV0tabzhQYnVHUVEKU0p4cFhVRUNnWUVBNnZhWDFQSm4yUUNxeCtET3k4SnZFNWp6WVBGcTlFR0FpRFg5VENTb3R4U1d2OTVOUVh0MQpBR1d1RmVETE1YSmZqejhibldwdVVuVzRqK0tNRnRaUDNMZ1Nmd3ZTUnVnNGVsQ1RHVEllWmJmQ0pqTGlrNVNPCjJ6SVBiK2xtL3Z3eXU5V3NkZEh3OVIyUjc3eWlzWkkxZlBDMndTTzZzRjlxT21rL2t1RGZibzBDZ1lFQXl1a1YKcjI3andnM2FpMEJLdTNhcmhSRExqbklzSnF5Qy9yUVJEUzlVQjVuTks1L1l4K1o4S0R5M2toMmV2YXNic1Q0cgozcFUxQXdKOGZDQnRXaC93YXRVYlNRZkNOQ0ppZ2hsbUpYcTJDeEVRUUllN1d5MGpNeWpINzJodVgwMGY0dFJWCkt1clVpK2V0SjA3NHBvbHNUaG9DUnZqbzdyaHREY3R3UmM2bUd4RUNnWUVBdUNoS1hJY1p5Y1Z5RlhNbjRpQWsKdXpGNElCVllCTldLRGpoeXJVbFdTeGlDQnlRUFhUR01SS0Z0VG94LzllTjA3bXRDRTZFbGtzL2R0amlVSUJvZApRaHVyczVQcVhkVUkzeVZrQmExNGtiVHpJTWxsT05LSkhWZ2hMVSs4Z0VIZTZjWFJoQTdtVXRlNFdEUjdONzRtCjJpUTR1U3h0MkdzUWNYT29kbEIyRHNrQ2dZRUF5VDZwZ2toaDNlbnRrZVNlK2hSMWd0RW9na3ZjWERNRzdPVGMKY0k0N01ocXBjWlhrNUVaRlo0Ym9yaU53ZUQ3SGhWL2JGTFE1VXBYWnJ5WmVMbCsxQzgvMmN0VWVHS1R0dklqQwpWWFBDTDNHcUE4WmEzTkFFdEUzREZrQW1ENkVuZWNvTCtqZlR2RHAzOHArUlgySzJwek9HaEt1RUlwZUptWC9uCkIyVXdPM0VDZ1lFQTRYK25CRkRsNEdXbFNQU2VKZzZ4L3JWUXFyZUkwcmIrSUViNGhGOVZBcndEc2Z6L0hnb3cKSGpDTkR0N2t0YlJVTzlsNTJaZ3YrbWlOS3Z5dnNCbUNQalA5d0k4Y2hUZlhOZ1ZIYzE5YzJLTWRxTkdIckw1YwpTdytHMkl3MFRjREpQeFZyNGxkN01NNFZtYmtIKzBUVEtyVThLWkZQc2o2RGYraS9mMSs0NEE0PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
================================================
FILE: examples/cert.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-cert
spec:
path: "keyvaluev1/vault.koudingspawn.de"
type: "CERT"
================================================
FILE: examples/certjks-rollout-redo.yml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
restartPolicy: Always
selector:
matchLabels:
app: nginx
---
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-rollout-redo-certjks
spec:
path: "keyvaluev1/vault.koudingspawn.de"
type: "CERTJKS"
changeAdjustmentCallback:
type: deployment
name: nginx
================================================
FILE: examples/certjks.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-certjks
spec:
path: "keyvaluev1/vault.koudingspawn.de"
type: "CERTJKS"
================================================
FILE: examples/dockercfg-error.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-dockercfg-error
spec:
path: "blub/docker-hub"
type: "DOCKERCFG"
================================================
FILE: examples/dockercfg.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-dockercfg
spec:
path: "keyvaluev1/docker-hub"
type: "DOCKERCFG"
================================================
FILE: examples/keyvalue.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-keyvalue
spec:
path: "keyvaluev1/docker-hub"
type: "KEYVALUE"
================================================
FILE: examples/keyvaluev2-version.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-keyvaluev2
spec:
path: "keyvaluev2/example"
type: "KEYVALUEV2"
versionConfiguration:
version: 2
================================================
FILE: examples/keyvaluev2.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-keyvaluev2
spec:
path: "keyvaluev2/example"
type: "KEYVALUEV2"
================================================
FILE: examples/kind/cluster.yaml
================================================
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30078
hostPort: 8200
================================================
FILE: examples/kind/run.sh
================================================
#!/usr/bin/env bash
### setup kind cluster
kind create cluster --config $PWD/cluster.yaml
### it exposes at 8200 a port for vault
### install vault with a static token
kind get kubeconfig > ~/.kube/kind_config
export KUBECONFIG="$HOME/.kube/kind_config"
kubectl create namespace vault
kubectl apply -f vault.yaml --namespace vault
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8200/ui/)" != "200" ]]; do sleep 5; done
echo "Vault is up and running"
export VAULT_ADDR="http://localhost:8200"
export VAULT_TOKEN="root"
### end: install vault with a static token
### deploy vault-crd
kubectl apply -f ../../deploy/rbac.yaml
kubectl apply -f ../../deploy/admission-webhook.yaml
### end: deploy vault-crd
### configure vault
vault secrets enable -version=1 --path=keyvaluev1 kv
echo "Configure vault with default values"
vault write keyvaluev1/docker-hub url=registry.gitlab.com username=username password=VERYSECURE email=john.doe@test.com
vault secrets enable -path=testpki -description=testpki pki
vault secrets tune -max-lease-ttl=8760h testpki
vault write testpki/root/generate/internal \
common_name=koudingspawn.de \
ttl=8500h
vault write testpki/roles/testrole \
allowed_domains=koudingspawn.de \
allow_subdomains=true \
max_ttl=200h
vault write -format=json testpki/issue/testrole common_name=vault.koudingspawn.de > data.json
vault write keyvaluev1/vault.koudingspawn.de @data.json
rm data.json
vault secrets enable -version=2 --path=keyvaluev2 kv
vault kv put keyvaluev2/example key=first-version value=first-version
vault kv put keyvaluev2/example key=second-version value=second-version
vault kv put keyvaluev2/example key=third-version value=third-version
vault kv put keyvaluev2/example key=fourth-version value=fourth-version
vault kv put keyvaluev2/database/root username=root password=really
vault write keyvaluev1/database/host host=localhost
### end: configure vault
================================================
FILE: examples/kind/vault.yaml
================================================
---
# Source: vault/templates/server-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault
namespace: vault
labels:
helm.sh/chart: vault-0.6.0
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
app.kubernetes.io/managed-by: Helm
---
# Source: vault/templates/server-clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: vault-server-binding
labels:
helm.sh/chart: vault-0.6.0
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
app.kubernetes.io/managed-by: Helm
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault
namespace: vault
---
# Source: vault/templates/server-headless-service.yaml
# Service for Vault cluster
apiVersion: v1
kind: Service
metadata:
name: vault-internal
namespace: vault
labels:
helm.sh/chart: vault-0.6.0
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
app.kubernetes.io/managed-by: Helm
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
spec:
clusterIP: None
publishNotReadyAddresses: true
ports:
- name: "http"
port: 8200
targetPort: 8200
- name: https-internal
port: 8201
targetPort: 8201
selector:
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
component: server
---
# Source: vault/templates/server-service.yaml
# Service for Vault cluster
apiVersion: v1
kind: Service
metadata:
name: vault
namespace: vault
labels:
helm.sh/chart: vault-0.6.0
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
app.kubernetes.io/managed-by: Helm
annotations:
# This must be set in addition to publishNotReadyAddresses due
# to an open issue where it may not work:
# https://github.com/kubernetes/kubernetes/issues/58662
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
spec:
type: NodePort
# We want the servers to become available even if they're not ready
# since this DNS is also used for join operations.
publishNotReadyAddresses: true
ports:
- name: http
port: 8200
targetPort: 8200
nodePort: 30078
- name: https-internal
port: 8201
targetPort: 8201
selector:
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
component: server
---
# Source: vault/templates/server-statefulset.yaml
# StatefulSet to run the actual vault server cluster.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: vault
namespace: vault
labels:
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
app.kubernetes.io/managed-by: Helm
spec:
serviceName: vault-internal
podManagementPolicy: Parallel
replicas: 1
updateStrategy:
type: OnDelete
selector:
matchLabels:
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
component: server
template:
metadata:
labels:
helm.sh/chart: vault-0.6.0
app.kubernetes.io/name: vault
app.kubernetes.io/instance: vault
component: server
spec:
terminationGracePeriodSeconds: 10
serviceAccountName: vault
securityContext:
runAsNonRoot: true
runAsGroup: 1000
runAsUser: 100
fsGroup: 1000
volumes:
- name: home
emptyDir: {}
containers:
- name: vault
image: vault:1.4.2
imagePullPolicy: IfNotPresent
command:
args:
env:
- name: HOST_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: VAULT_K8S_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: VAULT_K8S_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: VAULT_ADDR
value: "http://127.0.0.1:8200"
- name: VAULT_API_ADDR
value: "http://$(POD_IP):8200"
- name: SKIP_CHOWN
value: "true"
- name: SKIP_SETCAP
value: "true"
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: VAULT_CLUSTER_ADDR
value: "https://$(HOSTNAME).vault-internal:8201"
- name: HOME
value: "/home/vault"
- name: VAULT_DEV_ROOT_TOKEN_ID
value: "root"
volumeMounts:
- name: home
mountPath: /home/vault
ports:
- containerPort: 8200
name: http
- containerPort: 8201
name: https-internal
- containerPort: 8202
name: http-rep
readinessProbe:
# Check status; unsealed vault servers return 0
# The exit code reflects the seal status:
# 0 - unsealed
# 1 - error
# 2 - sealed
exec:
command: ["/bin/sh", "-ec", "vault status -tls-skip-verify"]
failureThreshold: 2
initialDelaySeconds: 5
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 5
lifecycle:
# Vault container doesn't receive SIGTERM from Kubernetes
# and after the grace period ends, Kube sends SIGKILL. This
# causes issues with graceful shutdowns such as deregistering itself
# from Consul (zombie services).
preStop:
exec:
command: [
"/bin/sh", "-c",
# Adding a sleep here to give the pod eviction a
# chance to propagate, so requests will not be made
# to this pod while it's terminating
"sleep 5 && kill -SIGTERM $(pidof vault)",
]
================================================
FILE: examples/pki.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-pki
spec:
path: "testpki/issue/testrole"
type: "PKI"
pkiConfiguration:
commonName: "vault.koudingspawn.de"
ttl: "7m"
================================================
FILE: examples/pki_chain.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-pki
spec:
path: "pki_int/issue/testrole"
type: "PKI"
pkiConfiguration:
commonName: "localhost"
ttl: "7m"
================================================
FILE: examples/pkijks.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: test-pkijks
spec:
path: "testpki/issue/testrole"
type: "PKIJKS"
jksConfiguration:
caAlias: CARoot
pkiConfiguration:
commonName: "vault.koudingspawn.de"
ttl: "7m"
================================================
FILE: examples/properties.yml
================================================
apiVersion: "koudingspawn.de/v1"
kind: Vault
metadata:
name: properties-example
spec:
type: "PROPERTIES"
propertiesConfiguration:
context:
contextKey: value
files:
test.properties: |
test={{ contextKey }}
datasource.username={{ vault.lookupV2('keyvaluev2/database/root').get('username') }}
datasource.password={{ vault.lookupV2('keyvaluev2/database/root').get('password') }}
datasource.host={{ vault.lookup('keyvaluev1/database/host', 'host') }}
================================================
FILE: mvnw
================================================
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
echo $MAVEN_PROJECTBASEDIR
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
================================================
FILE: mvnw.cmd
================================================
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.koudingspawn</groupId>
<artifactId>vault</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>vault-crd</name>
<description>Vault CRD for sharing Vault Secrets to Kubernetes</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<fabric8.version>6.8.1</fabric8.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>${fabric8.version}</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<dependency>
<groupId>com.hubspot.jinjava</groupId>
<artifactId>jinjava</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>3.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>17</release>
<fork>true</fork>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: readme.md
================================================
# What is Vault-CRD?
Vault-CRD is a custom resource definition for holding secrets that are stored in HashiCorp Vault up to date with
Kubernetes secrets.
The following Secret engines of Vault are supported:
* KV (Version 1)
* KV (Version 2)
* PKI
The following types of secrets can be managed by Vault-CRD:
* Docker Pull Secret (DockerCfg)
* Ingress Certificates
* JKS Key Stores
For more details please see: [https://vault.koudingspawn.de/how-does-vault-crd-work](https://vault.koudingspawn.de/how-does-vault-crd-work)
## Note
Due to Docker's decision to discontinue its Free Teams, I decided to host my Docker images on GHCR (GitHub Container
Registry) and public ECR (Elastic Container Registry) in the future.
================================================
FILE: src/main/java/de/koudingspawn/vault/Constants.java
================================================
package de.koudingspawn.vault;
public class Constants {
private Constants() {
}
public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm'Z'";
public static final String COMPARE_ANNOTATION = "/compare";
public static final String LAST_UPDATE_ANNOTATION = "/lastUpdated";
}
================================================
FILE: src/main/java/de/koudingspawn/vault/VaultApplication.java
================================================
package de.koudingspawn.vault;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class VaultApplication {
public static void main(String[] args) {
SpringApplication.run(VaultApplication.class, args);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/admissionreview/AdmissionReviewRestService.java
================================================
package de.koudingspawn.vault.admissionreview;
import io.fabric8.kubernetes.api.model.admission.v1.AdmissionResponse;
import io.fabric8.kubernetes.api.model.admission.v1.AdmissionReview;
import io.fabric8.kubernetes.api.model.admission.v1.AdmissionReviewBuilder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/validation/vault-crd")
public class AdmissionReviewRestService {
private final AdmissionReviewService admissionReviewService;
public AdmissionReviewRestService(AdmissionReviewService admissionReviewService) {
this.admissionReviewService = admissionReviewService;
}
@PostMapping
public AdmissionReview validate(@RequestBody AdmissionReview admissionRequest) {
AdmissionResponse admissionResponse = admissionReviewService.validate(admissionRequest.getRequest());
return new AdmissionReviewBuilder()
.withResponse(admissionResponse)
.build();
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/admissionreview/AdmissionReviewService.java
================================================
package de.koudingspawn.vault.admissionreview;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.vault.VaultService;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import io.fabric8.kubernetes.api.model.Status;
import io.fabric8.kubernetes.api.model.StatusBuilder;
import io.fabric8.kubernetes.api.model.admission.v1.AdmissionRequest;
import io.fabric8.kubernetes.api.model.admission.v1.AdmissionResponse;
import io.fabric8.kubernetes.api.model.admission.v1.AdmissionResponseBuilder;
import io.fabric8.kubernetes.client.utils.Serialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class AdmissionReviewService {
private static final Logger log = LoggerFactory.getLogger(AdmissionReviewService.class);
private final VaultService vaultService;
public AdmissionReviewService(VaultService vaultService) {
this.vaultService = vaultService;
}
public AdmissionResponse validate(AdmissionRequest admissionRequest) {
try {
String s = Serialization.asYaml(admissionRequest.getObject());
Vault vault = Serialization.unmarshal(s, Vault.class);
vaultService.generateSecret(vault);
} catch (ClassCastException ex) {
log.error("Received Admission Request of invalid type!");
return invalidRequest(admissionRequest.getUid(), "Received Admission Request of invalid type!");
} catch (SecretNotAccessibleException e) {
log.error("Admission Request failed with Secret not Accessible Exception", e);
return invalidRequest(admissionRequest.getUid(), e.getMessage());
}
return validRequest(admissionRequest.getUid());
}
private AdmissionResponse validRequest(String uuid) {
return new AdmissionResponseBuilder()
.withAllowed(true)
.withUid(uuid)
.build();
}
private AdmissionResponse invalidRequest(String uid, String message) {
Status status = new StatusBuilder()
.withCode(400)
.withMessage(message)
.build();
return new AdmissionResponseBuilder()
.withAllowed(false)
.withUid(uid)
.withStatus(status)
.build();
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/Vault.java
================================================
package de.koudingspawn.vault.crd;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.*;
import java.util.HashMap;
import java.util.Objects;
@Version(Vault.VERSION)
@Group(Vault.GROUP)
@Kind(Vault.KIND)
@Singular(Vault.SINGULAR)
@Plural(Vault.PLURAL)
public class Vault extends CustomResource<VaultSpec, Void> implements Namespaced {
public static final String GROUP = "koudingspawn.de";
public static final String VERSION = "v1";
public static final String KIND = "Vault";
public static final String SINGULAR = "vault";
public static final String PLURAL = "vault";
public boolean modifyHandlerEquals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Vault vault = (Vault) o;
// spec equals
if (vault.getSpec() == null && spec != null) return false;
if (vault.getSpec() != null && spec == null) return false;
if (vault.getSpec() != null && spec != null) {
if (!vault.getSpec().equals(spec)) return false;
} // null && null => true for spec
// metadata equals
if (vault.getMetadata() == null && getMetadata() == null) return true;
if (vault.getMetadata() == null) return false;
if (getMetadata() == null) return false;
// metadata.name, metadata.namespace, metadata.uid equals
if (!vault.getMetadata().getName().equals(getMetadata().getName())) return false;
if (!vault.getMetadata().getNamespace().equals(getMetadata().getNamespace())) return false;
if (!vault.getMetadata().getUid().equals(getMetadata().getUid())) return false;
// metadata.labels equals
if (vault.getMetadata().getLabels() == null && getMetadata().getLabels() != null) return false;
if (vault.getMetadata().getLabels() != null && getMetadata().getLabels() == null) return false;
if (!Objects.equals(vault.getMetadata().getLabels(), getMetadata().getLabels())) return false;
// metadata.annotations equals
if (vault.getMetadata().getAnnotations() == null && getMetadata().getAnnotations() != null) return false;
if (vault.getMetadata().getAnnotations() != null && getMetadata().getAnnotations() == null) return false;
if (vault.getMetadata().getAnnotations() != null && getMetadata().getAnnotations() != null) {
HashMap<String, String> vaultAnnotations = new HashMap<>(vault.getMetadata().getAnnotations());
vaultAnnotations.remove("kubectl.kubernetes.io/last-applied-configuration");
HashMap<String, String> annotations = new HashMap<>(getMetadata().getAnnotations());
annotations.remove("kubectl.kubernetes.io/last-applied-configuration");
return Objects.equals(vaultAnnotations, annotations);
}
return true;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultChangeAdjustmentCallback.java
================================================
package de.koudingspawn.vault.crd;
import java.util.Objects;
public class VaultChangeAdjustmentCallback {
private String type;
private String name;
public VaultChangeAdjustmentCallback() {
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return type + "/" + name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VaultChangeAdjustmentCallback that = (VaultChangeAdjustmentCallback) o;
return Objects.equals(type, that.type) && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(type, name);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultDockerCfgConfiguration.java
================================================
package de.koudingspawn.vault.crd;
import java.util.Objects;
public class VaultDockerCfgConfiguration {
private VaultType type;
private Integer version;
public VaultDockerCfgConfiguration() {
this.type = VaultType.KEYVALUE;
}
public VaultType getType() {
return type;
}
public void setType(VaultType version) {
this.type = version;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VaultDockerCfgConfiguration that = (VaultDockerCfgConfiguration) o;
return type == that.type && Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(type, version);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultJKSConfiguration.java
================================================
package de.koudingspawn.vault.crd;
import java.util.Objects;
public class VaultJKSConfiguration {
private String password;
private String alias;
private String keyName;
private String caAlias;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public String getCaAlias() {
return caAlias;
}
public void setCaAlias(String caAlias) {
this.caAlias = caAlias;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VaultJKSConfiguration that = (VaultJKSConfiguration) o;
return Objects.equals(password, that.password) && Objects.equals(alias, that.alias) && Objects.equals(keyName, that.keyName) && Objects.equals(caAlias, that.caAlias);
}
@Override
public int hashCode() {
return Objects.hash(password, alias, keyName, caAlias);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultList.java
================================================
package de.koudingspawn.vault.crd;
import io.fabric8.kubernetes.api.model.DefaultKubernetesResourceList;
public class VaultList extends DefaultKubernetesResourceList<Vault> {
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultPkiConfiguration.java
================================================
package de.koudingspawn.vault.crd;
import java.util.Objects;
public class VaultPkiConfiguration {
private String commonName;
private String altNames;
private String ipSans;
private String ttl;
public String getCommonName() {
return commonName;
}
public void setCommonName(String commonName) {
this.commonName = commonName;
}
public String getAltNames() {
return altNames;
}
public void setAltNames(String altNames) {
this.altNames = altNames;
}
public String getIpSans() {
return ipSans;
}
public void setIpSans(String ipSans) {
this.ipSans = ipSans;
}
public String getTtl() {
return ttl;
}
public void setTtl(String ttl) {
this.ttl = ttl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VaultPkiConfiguration that = (VaultPkiConfiguration) o;
return Objects.equals(commonName, that.commonName) && Objects.equals(altNames, that.altNames) && Objects.equals(ipSans, that.ipSans) && Objects.equals(ttl, that.ttl);
}
@Override
public int hashCode() {
return Objects.hash(commonName, altNames, ipSans, ttl);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultPropertiesConfiguration.java
================================================
package de.koudingspawn.vault.crd;
import java.util.HashMap;
import java.util.Objects;
public class VaultPropertiesConfiguration {
private HashMap<String, String> files;
private HashMap<String, String> context;
public HashMap<String, String> getFiles() {
return files;
}
public void setFiles(HashMap<String, String> files) {
this.files = files;
}
public HashMap<String, String> getContext() {
return context;
}
public void setContext(HashMap<String, String> context) {
this.context = context;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VaultPropertiesConfiguration that = (VaultPropertiesConfiguration) o;
return Objects.equals(files, that.files) && Objects.equals(context, that.context);
}
@Override
public int hashCode() {
return Objects.hash(files, context);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultSpec.java
================================================
package de.koudingspawn.vault.crd;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.fabric8.kubernetes.api.model.KubernetesResource;
import java.util.Objects;
@JsonDeserialize
@JsonInclude(JsonInclude.Include.NON_NULL)
public class VaultSpec implements KubernetesResource {
private String path;
private VaultType type;
private VaultPkiConfiguration pkiConfiguration;
private VaultJKSConfiguration jksConfiguration;
private VaultVersionedConfiguration versionConfiguration;
private VaultPropertiesConfiguration propertiesConfiguration;
private VaultDockerCfgConfiguration dockerCfgConfiguration;
private VaultChangeAdjustmentCallback changeAdjustmentCallback;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public VaultType getType() {
return type;
}
public void setType(VaultType type) {
this.type = type;
}
public VaultPkiConfiguration getPkiConfiguration() {
return pkiConfiguration;
}
public void setPkiConfiguration(VaultPkiConfiguration pkiConfiguration) {
this.pkiConfiguration = pkiConfiguration;
}
public VaultJKSConfiguration getJksConfiguration() {
return jksConfiguration;
}
public void setJksConfiguration(VaultJKSConfiguration jksConfiguration) {
this.jksConfiguration = jksConfiguration;
}
public VaultVersionedConfiguration getVersionConfiguration() {
return versionConfiguration;
}
public void setVersionConfiguration(VaultVersionedConfiguration versionConfiguration) {
this.versionConfiguration = versionConfiguration;
}
public VaultPropertiesConfiguration getPropertiesConfiguration() {
return propertiesConfiguration;
}
public void setPropertiesConfiguration(VaultPropertiesConfiguration propertiesConfiguration) {
this.propertiesConfiguration = propertiesConfiguration;
}
public VaultDockerCfgConfiguration getDockerCfgConfiguration() {
return dockerCfgConfiguration;
}
public void setDockerCfgConfiguration(VaultDockerCfgConfiguration dockerCfgConfiguration) {
this.dockerCfgConfiguration = dockerCfgConfiguration;
}
public VaultChangeAdjustmentCallback getChangeAdjustmentCallback() {
return changeAdjustmentCallback;
}
public void setChangeAdjustmentCallback(VaultChangeAdjustmentCallback changeAdjustmentCallback) {
this.changeAdjustmentCallback = changeAdjustmentCallback;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VaultSpec vaultSpec = (VaultSpec) o;
return Objects.equals(path, vaultSpec.path) && type == vaultSpec.type && Objects.equals(pkiConfiguration, vaultSpec.pkiConfiguration) && Objects.equals(jksConfiguration, vaultSpec.jksConfiguration) && Objects.equals(versionConfiguration, vaultSpec.versionConfiguration) && Objects.equals(propertiesConfiguration, vaultSpec.propertiesConfiguration) && Objects.equals(dockerCfgConfiguration, vaultSpec.dockerCfgConfiguration) && Objects.equals(changeAdjustmentCallback, vaultSpec.changeAdjustmentCallback);
}
@Override
public int hashCode() {
return Objects.hash(path, type, pkiConfiguration, jksConfiguration, versionConfiguration, propertiesConfiguration, dockerCfgConfiguration, changeAdjustmentCallback);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultType.java
================================================
package de.koudingspawn.vault.crd;
public enum VaultType {
PKI, CERT, DOCKERCFG, KEYVALUE, PKIJKS, CERTJKS, KEYVALUEV2, PROPERTIES
}
================================================
FILE: src/main/java/de/koudingspawn/vault/crd/VaultVersionedConfiguration.java
================================================
package de.koudingspawn.vault.crd;
import java.util.Objects;
public class VaultVersionedConfiguration {
private Integer version;
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VaultVersionedConfiguration that = (VaultVersionedConfiguration) o;
return Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(version);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/ChangeAdjustmentService.java
================================================
package de.koudingspawn.vault.kubernetes;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultChangeAdjustmentCallback;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class ChangeAdjustmentService {
private static final Logger log = LoggerFactory.getLogger(ChangeAdjustmentService.class);
private final KubernetesClient client;
public ChangeAdjustmentService(KubernetesClient client) {
this.client = client;
}
public void handle(Vault resource) {
VaultChangeAdjustmentCallback changeAdjustmentCallback = resource.getSpec().getChangeAdjustmentCallback();
if (changeAdjustmentCallback != null && changeAdjustmentCallback.getType() != null && changeAdjustmentCallback.getName() != null) {
switch (changeAdjustmentCallback.getType().toLowerCase()) {
case "deployment" ->
rotateDeployment(resource.getMetadata().getNamespace(), changeAdjustmentCallback.getName());
case "statefulset" ->
rotateStatefulSet(resource.getMetadata().getNamespace(), changeAdjustmentCallback.getName());
default ->
log.info("Currently a change adjustment is only supported for type deployment. Resource {} in namespace {} has type {}",
resource.getMetadata().getName(), resource.getMetadata().getNamespace(), changeAdjustmentCallback.getType());
}
} else {
log.warn("Change adjustment callback for resource {} in namespace {} is invalid!", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
}
}
private void rotateDeployment(String namespace, String name) {
try {
log.info("Start rotation of deployment {} in namespace {}", name, namespace);
client.apps()
.deployments()
.inNamespace(namespace)
.withName(name)
.edit(d -> new DeploymentBuilder(d)
.editSpec()
.editTemplate()
.editMetadata()
.addToAnnotations("certificate-change-on", "vault-crd_" + System.currentTimeMillis())
.endMetadata()
.endTemplate()
.endSpec()
.build());
} catch (Exception ex) {
log.error("Failed to rotate deployment {} in namespace {} with exception:", name, namespace, ex);
}
}
private void rotateStatefulSet(String namespace, String name) {
try {
log.info("Start rotation of statefulSet {} in namespace {}", name, namespace);
client.apps()
.statefulSets()
.inNamespace(namespace)
.withName(name)
.edit(statefulSet -> new StatefulSetBuilder(statefulSet)
.editSpec()
.editTemplate()
.editMetadata()
.addToAnnotations("certificate-change-on", "vault-crd_" + System.currentTimeMillis())
.endMetadata()
.endTemplate()
.endSpec()
.build());
} catch (Exception ex) {
log.error("Failed to rotate statefulSet {} in namespace {} with exception:", name, namespace, ex);
}
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/EventHandler.java
================================================
package de.koudingspawn.vault.kubernetes;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.event.EventNotification;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.VaultService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import static de.koudingspawn.vault.kubernetes.event.EventType.*;
@Component
public class EventHandler {
private static final Logger log = LoggerFactory.getLogger(EventHandler.class);
private final VaultService vaultService;
private final KubernetesService kubernetesService;
private final ChangeAdjustmentService changeAdjustmentService;
private final EventNotification eventNotification;
private final boolean fixOwnerReferenceEnabled;
public EventHandler(VaultService vaultService,
KubernetesService kubernetesService,
ChangeAdjustmentService changeAdjustmentService,
EventNotification eventNotification,
@Value("${kubernetes.ownerreference-fix.enabled:true}") boolean fixOwnerReferenceEnabled) {
this.vaultService = vaultService;
this.kubernetesService = kubernetesService;
this.changeAdjustmentService = changeAdjustmentService;
this.eventNotification = eventNotification;
this.fixOwnerReferenceEnabled = fixOwnerReferenceEnabled;
}
public void addHandler(Vault resource) {
if (!kubernetesService.exists(resource)) {
try {
VaultSecret secretContent = vaultService.generateSecret(resource);
kubernetesService.createSecret(resource, secretContent);
eventNotification.storeNewEvent(CREATION_SUCCESSFUL, "Successfully created secret", resource);
} catch (Exception e) {
log.error("Failed to generate secret for vault resource {} in namespace {} failed with exception:",
resource.getMetadata().getName(), resource.getMetadata().getNamespace(), e);
eventNotification.storeNewEvent(CREATION_FAILED, "Failed to generate secret with exception " + e.getMessage(), resource);
}
} else if (fixOwnerReferenceEnabled && kubernetesService.hasBrokenOwnerReference(resource)) {
log.info("Fix owner reference for secret {} in namespace {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
modifyHandler(resource);
eventNotification.storeNewEvent(FIXED_REFERENCE, "Fixed owner reference", resource);
}
}
void deleteHandler(Vault resource) {
kubernetesService.deleteSecret(resource.getMetadata());
eventNotification.storeNewEvent(DELETION, "Deleted secret for resource", resource);
}
public void modifyHandler(Vault resource) {
try {
VaultSecret secretContent = vaultService.generateSecret(resource);
kubernetesService.modifySecret(resource, secretContent);
eventNotification.storeNewEvent(MODIFICATION_SUCCESSFUL, "Successfully modified secret", resource);
if (resource.getSpec().getChangeAdjustmentCallback() != null) {
changeAdjustmentService.handle(resource);
eventNotification.storeNewEvent(ROTATION,
"Successfully started rotation of associated resource " + resource.getSpec().getChangeAdjustmentCallback().toString(), resource);
}
} catch (Exception e) {
log.error("Failed to modify secret for vault resource {} in namespace {} failed with exception:",
resource.getMetadata().getName(), resource.getMetadata().getNamespace(), e);
eventNotification.storeNewEvent(MODIFICATION_FAILED, "Modification failed " + e.getMessage(), resource);
}
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/KubernetesConnection.java
================================================
package de.koudingspawn.vault.kubernetes;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultList;
import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class KubernetesConnection {
private static final Logger log = LoggerFactory.getLogger(KubernetesConnection.class);
@Bean
@Profile("development")
public KubernetesClient testClient() {
Config config = new ConfigBuilder().withMasterUrl("http://localhost:8001").withWatchReconnectLimit(5).build();
return new KubernetesClientBuilder()
.withConfig(config)
.build();
}
@Bean
@Profile("!development")
public KubernetesClient client() {
return new KubernetesClientBuilder().build();
}
@Bean
public MixedOperation<Vault, VaultList, Resource<Vault>> customResource(
KubernetesClient client,
@Value("${kubernetes.crd.name}") String crdName) {
Resource<CustomResourceDefinition> crdResource = client.apiextensions().v1().customResourceDefinitions().withName(crdName);
CustomResourceDefinition customResourceDefinition = crdResource.get();
if (customResourceDefinition == null) {
log.error("Please first apply custom resource definition and then restart vault-crd");
System.exit(1);
}
return client.resources(Vault.class, VaultList.class);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/KubernetesService.java
================================================
package de.koudingspawn.vault.kubernetes;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.cache.SecretCache;
import de.koudingspawn.vault.vault.VaultSecret;
import io.fabric8.kubernetes.api.model.DeletionPropagation;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.OwnerReference;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import static de.koudingspawn.vault.Constants.COMPARE_ANNOTATION;
import static de.koudingspawn.vault.Constants.LAST_UPDATE_ANNOTATION;
@Component
public class KubernetesService {
private static final Logger log = LoggerFactory.getLogger(KubernetesService.class);
private final KubernetesClient client;
private final String crdName;
private final String crdGroup;
private final SecretCache secretCache;
public KubernetesService(KubernetesClient client,
SecretCache secretCache,
@Value("${kubernetes.crd.name}") String crdName,
@Value("${kubernetes.crd.group}") String crdGroup) {
this.client = client;
this.crdName = crdName;
this.crdGroup = crdGroup;
this.secretCache = secretCache;
}
boolean exists(Vault resource) {
return getSecretByVault(resource) != null;
}
private Secret newSecretInstance(Vault resource, VaultSecret vaultSecret) {
Secret secret = new Secret();
secret.setType(vaultSecret.getType());
secret.setMetadata(metaData(resource.getMetadata(), vaultSecret.getCompare()));
secret.setData(vaultSecret.getData());
return secret;
}
void createSecret(Vault resource, VaultSecret vaultSecret) {
Secret secret = newSecretInstance(resource, vaultSecret);
secretCache.invalidate(secret.getMetadata().getNamespace(), secret.getMetadata().getName());
client.secrets().inNamespace(resource.getMetadata().getNamespace()).resource(secret).create();
log.info("Created secret for vault resource {} in namespace {}", secret.getMetadata().getName(), secret.getMetadata().getNamespace());
}
void deleteSecret(ObjectMeta resourceMetadata) {
secretCache.invalidate(resourceMetadata.getNamespace(), resourceMetadata.getName());
client.secrets().inNamespace(resourceMetadata.getNamespace()).withName(resourceMetadata.getName()).withPropagationPolicy(DeletionPropagation.BACKGROUND).delete();
log.info("Deleted secret {} in namespace {}", resourceMetadata.getName(), resourceMetadata.getNamespace());
}
void modifySecret(Vault resource, VaultSecret vaultSecret) {
Resource<Secret> secretResource = client.secrets().inNamespace(resource.getMetadata().getNamespace()).withName(resource.getMetadata().getName());
Secret secret;
if (secretResource.get() != null) {
secret = secretResource.get();
} else {
secret = newSecretInstance(resource, vaultSecret);
}
secret.setType(vaultSecret.getType());
secret.setMetadata(metaData(resource.getMetadata(), vaultSecret.getCompare()));
secret.setData(vaultSecret.getData());
secretCache.invalidate(resource.getMetadata().getNamespace(), resource.getMetadata().getName());
client.secrets().inNamespace(resource.getMetadata().getNamespace()).resource(secret).createOrReplace();
log.info("Modified secret {} in namespace {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
}
public Secret getSecretByVault(Vault resource) {
return secretCache.get(resource.getMetadata().getNamespace(), resource.getMetadata().getName());
}
private ObjectMeta metaData(ObjectMeta resource, String compare) {
ObjectMeta meta = new ObjectMeta();
meta.setNamespace(resource.getNamespace());
meta.setName(resource.getName());
if (resource.getLabels() != null) {
meta.setLabels(resource.getLabels());
}
if (meta.getLabels() == null) {
meta.setLabels(new HashMap<>());
}
meta.getLabels().put(crdName, "vault");
HashMap<String, String> annotations = new HashMap<>();
if (resource.getAnnotations() != null) {
annotations.putAll(resource.getAnnotations());
}
annotations.put(crdName + LAST_UPDATE_ANNOTATION, LocalDateTime.now().toString());
annotations.put(crdName + COMPARE_ANNOTATION, compare);
meta.setAnnotations(annotations);
meta.setOwnerReferences(getOwnerReference(resource));
return meta;
}
private List<OwnerReference> getOwnerReference(ObjectMeta resource) {
boolean blockOwnerDeletion = false;
boolean controller = true;
OwnerReference owner = new OwnerReference(
crdGroup + "/v1",
blockOwnerDeletion,
controller,
"Vault",
resource.getName(),
resource.getUid()
);
ArrayList<OwnerReference> owners = new ArrayList<>();
owners.add(owner);
return owners;
}
public boolean hasBrokenOwnerReference(Vault resource) {
Resource<Secret> secretResource = client.secrets().inNamespace(resource.getMetadata().getNamespace()).withName(resource.getMetadata().getName());
if (secretResource.get() != null) {
Secret secret = secretResource.get();
if (secret.getMetadata() != null && secret.getMetadata().getOwnerReferences() != null && secret.getMetadata().getOwnerReferences().size() == 1) {
OwnerReference ownerReference = secret.getMetadata().getOwnerReferences().get(0);
return ownerReference.getApiVersion().equals(crdName + "/v1");
}
}
return false;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/Watcher.java
================================================
package de.koudingspawn.vault.kubernetes;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.scheduler.ScheduledRefresh;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
import io.fabric8.kubernetes.client.informers.SharedIndexInformer;
import io.fabric8.kubernetes.client.informers.SharedInformerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import java.util.concurrent.TimeUnit;
@Configuration
@Profile("!test")
public class Watcher {
private static final Logger log = LoggerFactory.getLogger(Watcher.class);
private final EventHandler eventHandler;
private final KubernetesClient client;
private final ScheduledRefresh scheduledRefresh;
private final long resyncIntervalSecond;
public Watcher(EventHandler eventHandler, KubernetesClient client, ScheduledRefresh scheduledRefresh,
@Value("${kubernetes.interval}") long resyncIntervalSecond) {
this.eventHandler = eventHandler;
this.client = client;
this.scheduledRefresh = scheduledRefresh;
this.resyncIntervalSecond = resyncIntervalSecond;
}
@Bean
CommandLineRunner watchForResource() {
return (args) -> run();
}
private void run() {
SharedInformerFactory sharedInformerFactory = client.informers();
SharedIndexInformer<Vault> vaultInformer = sharedInformerFactory.sharedIndexInformerFor(Vault.class, TimeUnit.SECONDS.toMillis(resyncIntervalSecond));
vaultInformer.addEventHandler(
new ResourceEventHandler<>() {
@Override
public void onAdd(Vault resource) {
log.info("Received add for {} in namespace {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
eventHandler.addHandler(resource);
}
@Override
public void onUpdate(Vault oldObj, Vault resource) {
if (oldObj.modifyHandlerEquals(resource)) {
log.info("Received scheduled refresh for {} in namespace {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
scheduledRefresh.refreshVaultResource(resource);
} else {
log.info("Received update for {} in namespace {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
eventHandler.modifyHandler(resource);
}
}
@Override
public void onDelete(Vault resource, boolean deletedFinalStateUnknown) {
log.info("Received delete for {} in namespace {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
eventHandler.deleteHandler(resource);
}
}
);
sharedInformerFactory.addSharedInformerEventListener(ex ->
log.error("Exception occurred in shared informer, but caught: {}", ex.getMessage()));
log.info("Starting informer");
sharedInformerFactory.startAllRegisteredInformers();
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/cache/SecretCache.java
================================================
package de.koudingspawn.vault.kubernetes.cache;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
public class SecretCache {
private static final Logger log = LoggerFactory.getLogger(SecretCache.class);
private final Cache<String, Secret> secretResourceCache = Caffeine.newBuilder().build();
private final KubernetesClient client;
public SecretCache(KubernetesClient client, boolean watch) {
this.client = client;
if (watch) {
this.watcher();
}
}
public void watcher() {
client.secrets().inAnyNamespace().withLabel("vault.koudingspawn.de=vault").inform(
new ResourceEventHandler<>() {
private String cacheKey(String namespace, String name) {
return "%s/%s".formatted(namespace, name);
}
@Override
public void onAdd(Secret obj) {
String key = cacheKey(obj.getMetadata().getNamespace(), obj.getMetadata().getName());
log.debug("Received create secret for {}", key);
secretResourceCache.put(key, obj);
}
@Override
public void onUpdate(Secret oldObj, Secret newObj) {
String key = cacheKey(newObj.getMetadata().getNamespace(), newObj.getMetadata().getName());
log.debug("Received update for secret {}", key);
secretResourceCache.put(key, newObj);
}
@Override
public void onDelete(Secret obj, boolean deletedFinalStateUnknown) {
String key = cacheKey(obj.getMetadata().getNamespace(), obj.getMetadata().getName());
log.debug("Invalidate secret cache for {} after delete", key);
secretResourceCache.invalidate(key);
}
}, TimeUnit.MINUTES.toMillis(60));
}
public Secret get(String namespace, String name) {
String key = String.format("%s/%s", namespace, name);
Secret cacheSecret = secretResourceCache.getIfPresent(key);
if (cacheSecret != null) {
return cacheSecret;
}
Secret clusterSecret = client.secrets().inNamespace(namespace).withName(name).get();
if (clusterSecret != null) {
secretResourceCache.put(key, clusterSecret);
}
return clusterSecret;
}
public void invalidate(String namespace, String name) {
String key = String.format("%s/%s", namespace, name);
log.debug("Invalidate secret cache for {}", key);
secretResourceCache.invalidate(key);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/cache/SecretCacheConfiguration.java
================================================
package de.koudingspawn.vault.kubernetes.cache;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class SecretCacheConfiguration {
@Bean
@Profile("!test")
public SecretCache secretCache(KubernetesClient client) {
return new SecretCache(client, true);
}
@Bean
@Profile("test")
public SecretCache testSecretCache(KubernetesClient client) {
return new SecretCache(client, false);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/event/EventNotification.java
================================================
package de.koudingspawn.vault.kubernetes.event;
import de.koudingspawn.vault.crd.Vault;
import io.fabric8.kubernetes.api.model.Event;
import io.fabric8.kubernetes.api.model.EventBuilder;
import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.api.model.ObjectReferenceBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
@Service
public class EventNotification {
private static final Logger log = LoggerFactory.getLogger(EventNotification.class);
private final String crdGroup;
private final KubernetesClient client;
public EventNotification(@Value("${kubernetes.crd.group}") String crdGroup, KubernetesClient client) {
this.crdGroup = crdGroup;
this.client = client;
}
public void storeNewEvent(EventType type, String message, Vault resource) {
// @deprecated in 1.25 event v1beta1 will be deprecated
ObjectReference ref = new ObjectReferenceBuilder()
.withName(resource.getMetadata().getName())
.withNamespace(resource.getMetadata().getNamespace())
.withApiVersion(crdGroup + "/v1")
.withKind("Vault")
.withUid(resource.getMetadata().getUid())
.build();
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // Quoted "Z" to indicate UTC, no timezone offset
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());
Event evt = new EventBuilder()
.withNewMetadata()
.withGenerateName(resource.getMetadata().getName())
.withNamespace(resource.getMetadata().getNamespace())
.endMetadata()
.withInvolvedObject(ref)
.withLastTimestamp(nowAsISO)
.withFirstTimestamp(nowAsISO)
.withReportingComponent("vault-crd")
.withType(type.getEventType())
.withReason(type.getReason())
.withMessage(message)
.build();
try {
client.v1().events().inNamespace(resource.getMetadata().getNamespace()).resource(evt).create();
} catch (Exception ex) {
log.error("Failed to store event for {} in namespace {} next to resource with error",
resource.getMetadata().getName(), resource.getMetadata().getNamespace(), ex);
}
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/event/EventType.java
================================================
package de.koudingspawn.vault.kubernetes.event;
public enum EventType {
CREATION_SUCCESSFUL("Normal", "SuccessfulCreated"),
CREATION_FAILED("Failure", "FailedCreation"),
MODIFICATION_SUCCESSFUL("Normal", "SuccessfulModified"),
MODIFICATION_FAILED("Failure", "FailedModification"),
ROTATION("Rotation", "RotationTriggered"),
FIXED_REFERENCE("Normal", "FixedOwnerReference"),
DELETION("Normal", "DeletionOfResource");
private final String type;
private final String reason;
EventType(String type, String reason) {
this.type = type;
this.reason = reason;
}
public String getEventType() {
return type;
}
public String getReason() {
return reason;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/RefreshConfiguration.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler;
import org.springframework.beans.factory.config.ServiceLocatorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RefreshConfiguration {
@Bean("typeRefreshFactory")
public ServiceLocatorFactoryBean slfbForTypeRefresh() {
ServiceLocatorFactoryBean slfb = new ServiceLocatorFactoryBean();
slfb.setServiceLocatorInterface(TypeRefreshFactory.class);
return slfb;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/RequiresRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
public interface RequiresRefresh {
boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleException;
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/ScheduledRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.EventHandler;
import de.koudingspawn.vault.kubernetes.event.EventNotification;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import static de.koudingspawn.vault.kubernetes.event.EventType.MODIFICATION_FAILED;
@Component
public class ScheduledRefresh {
private static final Logger log = LoggerFactory.getLogger(ScheduledRefresh.class);
private final TypeRefreshFactory typeRefreshFactory;
private final EventHandler eventHandler;
private final EventNotification eventNotification;
public ScheduledRefresh(
EventHandler eventHandler,
TypeRefreshFactory typeRefreshFactory,
EventNotification eventNotification) {
this.typeRefreshFactory = typeRefreshFactory;
this.eventHandler = eventHandler;
this.eventNotification = eventNotification;
}
public void refreshVaultResource(Vault resource) {
RequiresRefresh requiresRefresh = typeRefreshFactory.get(resource.getSpec().getType().toString());
try {
if (requiresRefresh.refreshIsNeeded(resource)) {
log.info("Executing scheduled refresh for {} in namespace {}", resource.getMetadata().getName(), resource.getMetadata().getNamespace());
eventHandler.modifyHandler(resource);
}
} catch (SecretNotAccessibleException e) {
log.info("Refresh of secret {} in namespace {} failed with exception", resource.getMetadata().getName(), resource.getMetadata().getNamespace(), e);
eventNotification.storeNewEvent(MODIFICATION_FAILED, "Modification of secret failed with exception " + e.getMessage(), resource);
}
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/TypeRefreshFactory.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler;
public interface TypeRefreshFactory {
RequiresRefresh get(String name);
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CertJksRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import org.springframework.stereotype.Component;
@Component("CERTJKS")
public class CertJksRefresh implements RequiresRefresh {
private final CertRefresh certRefresh;
public CertJksRefresh(CertRefresh certRefresh) {
this.certRefresh = certRefresh;
}
@Override
public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleException {
return certRefresh.refreshIsNeeded(resource);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CertRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.KubernetesService;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.TypedSecretGeneratorFactory;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import io.fabric8.kubernetes.api.model.Secret;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("CERT")
public class CertRefresh extends CompareHash implements RequiresRefresh {
private final String crdName;
private final KubernetesService kubernetesService;
private final TypedSecretGeneratorFactory typedSecretGeneratorFactory;
public CertRefresh(@Value("${kubernetes.crd.name}") String crdName,
KubernetesService kubernetesService,
TypedSecretGeneratorFactory typedSecretGeneratorFactory) {
this.crdName = crdName;
this.typedSecretGeneratorFactory = typedSecretGeneratorFactory;
this.kubernetesService = kubernetesService;
}
@Override
public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleException {
return certHashHasChanged(resource);
}
private boolean certHashHasChanged(Vault resource) throws SecretNotAccessibleException {
Secret secretByVault = kubernetesService.getSecretByVault(resource);
TypedSecretGenerator certGenerator = typedSecretGeneratorFactory.get("CERTGENERATOR");
String vaultSha256 = certGenerator.getHash(resource.getSpec());
return super.hashHasChanged(secretByVault, vaultSha256, crdName);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CompareHash.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import io.fabric8.kubernetes.api.model.Secret;
import org.springframework.util.StringUtils;
import static de.koudingspawn.vault.Constants.COMPARE_ANNOTATION;
abstract public class CompareHash {
boolean hashHasChanged(Secret secretByVault, String vaultSha256, String crdName) {
if (secretByVault == null) {
// secret is not available...
return true;
}
if (secretByVault.getMetadata().getAnnotations() != null) {
String kubernetesSha256 = secretByVault.getMetadata().getAnnotations().get(crdName + COMPARE_ANNOTATION);
if (!StringUtils.hasText(kubernetesSha256)) {
// has no sha256 then calculate it
return true;
}
// check if vault and kubernetes are identical
return !vaultSha256.equals(kubernetesSha256);
}
return true;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/DockerCfgRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.KubernetesService;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.TypedSecretGeneratorFactory;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import io.fabric8.kubernetes.api.model.Secret;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("DOCKERCFG")
public class DockerCfgRefresh extends CompareHash implements RequiresRefresh {
private final String crdName;
private final KubernetesService kubernetesService;
private final TypedSecretGeneratorFactory typedSecretGeneratorFactory;
public DockerCfgRefresh(@Value("${kubernetes.crd.name}") String crdName,
KubernetesService kubernetesService,
TypedSecretGeneratorFactory typedSecretGeneratorFactory) {
this.crdName = crdName;
this.kubernetesService = kubernetesService;
this.typedSecretGeneratorFactory = typedSecretGeneratorFactory;
}
public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleException {
return dockerCfgHashHasChanged(resource);
}
private boolean dockerCfgHashHasChanged(Vault resource) throws SecretNotAccessibleException {
Secret secretByVault = kubernetesService.getSecretByVault(resource);
TypedSecretGenerator dockercfg = typedSecretGeneratorFactory.get("DOCKERCFGGENERATOR");
String vaultSha256 = dockercfg.getHash(resource.getSpec());
return super.hashHasChanged(secretByVault, vaultSha256, crdName);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/KeyValueRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.KubernetesService;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import de.koudingspawn.vault.vault.TypedSecretGeneratorFactory;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import io.fabric8.kubernetes.api.model.Secret;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("KEYVALUE")
public class KeyValueRefresh extends CompareHash implements RequiresRefresh {
private final String crdName;
private final KubernetesService kubernetesService;
private final TypedSecretGeneratorFactory typedSecretGeneratorFactory;
public KeyValueRefresh(@Value("${kubernetes.crd.name}") String crdName,
KubernetesService kubernetesService,
TypedSecretGeneratorFactory typedSecretGeneratorFactory) {
this.crdName = crdName;
this.kubernetesService = kubernetesService;
this.typedSecretGeneratorFactory = typedSecretGeneratorFactory;
}
public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleException {
return certHashHasChanged(resource);
}
private boolean certHashHasChanged(Vault resource) throws SecretNotAccessibleException {
Secret secretByVault = kubernetesService.getSecretByVault(resource);
String vaultSha256 = typedSecretGeneratorFactory.get("KEYVALUEGENERATOR").getHash(resource.getSpec());
return super.hashHasChanged(secretByVault, vaultSha256, crdName);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/KeyValueV2Refresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.KubernetesService;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import de.koudingspawn.vault.vault.TypedSecretGeneratorFactory;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import io.fabric8.kubernetes.api.model.Secret;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("KEYVALUEV2")
public class KeyValueV2Refresh extends CompareHash implements RequiresRefresh {
private final String crdName;
private final KubernetesService kubernetesService;
private final TypedSecretGeneratorFactory typedSecretGeneratorFactory;
public KeyValueV2Refresh(@Value("${kubernetes.crd.name}") String crdName,
KubernetesService kubernetesService,
TypedSecretGeneratorFactory typedSecretGeneratorFactory) {
this.crdName = crdName;
this.kubernetesService = kubernetesService;
this.typedSecretGeneratorFactory = typedSecretGeneratorFactory;
}
public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleException {
return hashHasChanged(resource);
}
private boolean hashHasChanged(Vault resource) throws SecretNotAccessibleException {
Secret secretByVault = kubernetesService.getSecretByVault(resource);
String vaultSha256 = typedSecretGeneratorFactory.get("KEYVALUEV2GENERATOR").getHash(resource.getSpec());
return super.hashHasChanged(secretByVault, vaultSha256, crdName);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PkiJksRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import org.springframework.stereotype.Component;
@Component("PKIJKS")
public class PkiJksRefresh implements RequiresRefresh {
private final PkiRefresh pkiRefresh;
public PkiJksRefresh(PkiRefresh pkiRefresh) {
this.pkiRefresh = pkiRefresh;
}
@Override
public boolean refreshIsNeeded(Vault resource) {
return pkiRefresh.refreshIsNeeded(resource);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PkiRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.KubernetesService;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import io.fabric8.kubernetes.api.model.Secret;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;
import java.util.TimeZone;
import static de.koudingspawn.vault.Constants.COMPARE_ANNOTATION;
import static de.koudingspawn.vault.Constants.DATE_FORMAT;
@Component("PKI")
public class PkiRefresh implements RequiresRefresh {
private static final Logger log = LoggerFactory.getLogger(PkiRefresh.class);
private final int interval;
private final KubernetesService kubernetesService;
private final String crdName;
public PkiRefresh(@Value("${kubernetes.interval}") int interval, @Value("${kubernetes.crd.name}") String crdName, KubernetesService kubernetesService) {
this.interval = interval;
this.kubernetesService = kubernetesService;
this.crdName = crdName;
}
public boolean refreshIsNeeded(Vault resource) {
Secret secretByVault = kubernetesService.getSecretByVault(resource);
return secretByVault == null || certificateIsNearExpirationDate(secretByVault);
}
private boolean certificateIsNearExpirationDate(Secret secretByVault) {
if (secretByVault.getMetadata().getAnnotations() != null) {
String expiration = secretByVault.getMetadata().getAnnotations().get(crdName + COMPARE_ANNOTATION);
Optional<Date> expirationDate = parseDate(expiration);
if (expirationDate.isPresent()) {
Date nextIntervals = new Date();
nextIntervals.setTime(nextIntervals.getTime() + (interval * 1000 * 5));
return nextIntervals.after(expirationDate.get());
} else {
log.error("Failed to parse date of secret {} in namespace {}", secretByVault.getMetadata().getName(), secretByVault.getMetadata().getNamespace());
}
}
return true;
}
private Optional<Date> parseDate(String date) {
if (date == null) {
return Optional.empty();
}
try {
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
TimeZone tz = TimeZone.getTimeZone("UTC");
format.setTimeZone(tz);
return Optional.of(format.parse(date));
} catch (ParseException e) {
return Optional.empty();
}
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PropertiesRefresh.java
================================================
package de.koudingspawn.vault.kubernetes.scheduler.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.kubernetes.scheduler.RequiresRefresh;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import org.springframework.stereotype.Component;
@Component("PROPERTIES")
public class PropertiesRefresh implements RequiresRefresh {
@Override
public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleException {
//TODO: allow properties refresh
return false;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/TypedSecretGenerator.java
================================================
package de.koudingspawn.vault.vault;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
public interface TypedSecretGenerator {
VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException;
String getHash(VaultSpec spec) throws SecretNotAccessibleException;
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/TypedSecretGeneratorFactory.java
================================================
package de.koudingspawn.vault.vault;
public interface TypedSecretGeneratorFactory {
TypedSecretGenerator get(String name);
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/VaultCommunication.java
================================================
package de.koudingspawn.vault.vault;
import de.koudingspawn.vault.crd.VaultDockerCfgConfiguration;
import de.koudingspawn.vault.crd.VaultPkiConfiguration;
import de.koudingspawn.vault.crd.VaultType;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.communication.TokenLookup;
import de.koudingspawn.vault.vault.impl.dockercfg.PullSecret;
import de.koudingspawn.vault.vault.impl.pki.PKIRequest;
import de.koudingspawn.vault.vault.impl.pki.PKIResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.vault.VaultException;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultVersionedKeyValueOperations;
import org.springframework.vault.support.VaultResponseSupport;
import org.springframework.vault.support.Versioned;
import org.springframework.vault.support.Versioned.Version;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import java.util.HashMap;
import java.util.Optional;
import java.util.regex.Pattern;
@Component
public class VaultCommunication {
private static final Logger log = LoggerFactory.getLogger(VaultCommunication.class);
private static final Pattern keyValuePattern = Pattern.compile("^.*?\\/.*?$");
private final VaultTemplate vaultTemplate;
public VaultCommunication(VaultTemplate vaultTemplate) {
this.vaultTemplate = vaultTemplate;
}
public PKIResponse createPki(String path, VaultPkiConfiguration configuration) throws SecretNotAccessibleException {
PKIRequest pkiRequest = generateRequest(configuration);
HttpEntity<PKIRequest> requestEntity = new HttpEntity<>(pkiRequest);
try {
return vaultTemplate.doWithSession(restOperations -> restOperations.postForObject(path, requestEntity, PKIResponse.class));
} catch (HttpStatusCodeException exception) {
int statusCode = exception.getStatusCode().value();
throw new SecretNotAccessibleException(
String.format("Couldn't generate pki secret from vault path %s status code %d", path, statusCode));
} catch (RestClientException ex) {
throw new SecretNotAccessibleException("Couldn't communicate with vault", ex);
}
}
public PKIResponse getCert(String path) throws SecretNotAccessibleException {
return getRequest(path, PKIResponse.class);
}
public PullSecret getDockerCfg(String path, VaultDockerCfgConfiguration dockerCfgConfiguration) throws SecretNotAccessibleException {
if (dockerCfgConfiguration.getType().equals(VaultType.KEYVALUE)) {
return getRequest(path, PullSecret.class);
} else {
return getVersionedSecret(path, Optional.ofNullable(dockerCfgConfiguration.getVersion()), PullSecret.class);
}
}
public HashMap getKeyValue(String path) throws SecretNotAccessibleException {
return getRequest(path, HashMap.class);
}
private <T> T getRequest(String path, Class<T> clazz) throws SecretNotAccessibleException {
try {
VaultResponseSupport<T> response = vaultTemplate.read(path, clazz);
if (response != null) {
return response.getData();
} else {
throw new SecretNotAccessibleException(String.format("The secret %s is not available or in the wrong format.", path));
}
} catch (VaultException exception) {
throw new SecretNotAccessibleException(
String.format("Couldn't load secret from vault path %s", path), exception);
}
}
private PKIRequest generateRequest(VaultPkiConfiguration configuration) {
PKIRequest pkiRequest = new PKIRequest();
if (configuration != null) {
if (StringUtils.hasText(configuration.getCommonName())) {
pkiRequest.setCommon_name(configuration.getCommonName());
}
if (StringUtils.hasText(configuration.getAltNames())) {
pkiRequest.setAlt_names(configuration.getAltNames());
}
if (StringUtils.hasText(configuration.getIpSans())) {
pkiRequest.setIp_sans(configuration.getIpSans());
}
if (StringUtils.hasText(configuration.getTtl())) {
pkiRequest.setTtl(configuration.getTtl());
}
}
return pkiRequest;
}
public HashMap getVersionedSecret(String path, Optional<Integer> version) throws SecretNotAccessibleException {
return getVersionedSecret(path, version, HashMap.class);
}
private <T> T getVersionedSecret(String path, Optional<Integer> version, Class<T> clazz) throws SecretNotAccessibleException {
String mountPoint = extractMountPoint(path);
String extractedKey = extractKey(path);
VaultVersionedKeyValueOperations versionedKV = vaultTemplate.opsForVersionedKeyValue(mountPoint);
Versioned<T> versionedResponse;
try {
if (version.isPresent()) {
versionedResponse = versionedKV.get(extractedKey, Version.from(version.get()), clazz);
} else {
versionedResponse = versionedKV.get(extractedKey, clazz);
}
if (versionedResponse != null) {
return versionedResponse.getData();
}
throw new SecretNotAccessibleException(String.format("The secret %s is not available or in the wrong format.", path));
} catch (VaultException ex) {
throw new SecretNotAccessibleException(
String.format("Couldn't load secret from vault path %s", path), ex);
}
}
public boolean isHealthy() {
return vaultTemplate.doWithSession(this::doWithRestOperations);
}
private boolean doWithRestOperations(RestOperations restOperations) {
try {
ResponseEntity<TokenLookup> healthEntity = restOperations.getForEntity("/auth/token/lookup-self", TokenLookup.class);
return healthEntity.getStatusCode().is2xxSuccessful();
} catch (RestClientException ex) {
log.error("Vault health check failed!", ex);
return false;
}
}
private String extractMountPoint(String path) throws SecretNotAccessibleException {
if (keyValuePattern.matcher(path).matches()) {
return path.split("/", 2)[0];
}
throw new SecretNotAccessibleException(String.format("Could not extract mountpoint from path: %s. A valid path looks like 'mountpoint/key'", path));
}
private String extractKey(String path) throws SecretNotAccessibleException {
if (keyValuePattern.matcher(path).matches()) {
return path.split("/", 2)[1];
}
throw new SecretNotAccessibleException(String.format("Could not extract key from path: %s. A valid path looks like 'mountpoint/key'", path));
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/VaultConfiguration.java
================================================
package de.koudingspawn.vault.vault;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ServiceLocatorFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.KubernetesAuthentication;
import org.springframework.vault.authentication.KubernetesAuthenticationOptions;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.AbstractVaultConfiguration;
import java.net.URI;
@Configuration
public class VaultConfiguration {
@Bean
public ServiceLocatorFactoryBean slfbForTypeRefresh() {
ServiceLocatorFactoryBean slfb = new ServiceLocatorFactoryBean();
slfb.setServiceLocatorInterface(TypedSecretGeneratorFactory.class);
return slfb;
}
@Configuration
@ConditionalOnProperty(name = "kubernetes.vault.auth", havingValue = "token")
class VaultTokenConnection extends AbstractVaultConfiguration {
private final String vaultToken;
private final String vaultUrl;
VaultTokenConnection(@Value("${kubernetes.vault.token}") String vaultToken,
@Value("${kubernetes.vault.url}") String vaultUrl) {
this.vaultToken = vaultToken;
this.vaultUrl = vaultUrl;
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.from(getVaultUrlWithoutPath(vaultUrl));
}
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication(vaultToken);
}
}
@Configuration
@ConditionalOnProperty(name = "kubernetes.vault.auth", havingValue = "serviceAccount")
class VaultServiceAccountConnection extends AbstractVaultConfiguration {
private final String vaultUrl;
private final String role;
private final String path;
VaultServiceAccountConnection(@Value("${kubernetes.vault.url}") String vaultUrl,
@Value("${kubernetes.vault.role}") String role,
@Value("${kubernetes.vault.path:kubernetes}") String path) {
this.vaultUrl = vaultUrl;
this.role = role;
this.path = path;
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.from(getVaultUrlWithoutPath(vaultUrl));
}
@Override
public ClientAuthentication clientAuthentication() {
KubernetesAuthenticationOptions options =
KubernetesAuthenticationOptions.builder().path(path).role(role).build();
return new KubernetesAuthentication(options, restOperations());
}
}
private URI getVaultUrlWithoutPath(String vaultUrl) {
return URI.create(vaultUrl.replace("/v1/", ""));
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/VaultHealthCheck.java
================================================
package de.koudingspawn.vault.vault;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class VaultHealthCheck implements HealthIndicator {
private final VaultCommunication vaultCommunication;
public VaultHealthCheck(VaultCommunication vaultCommunication) {
this.vaultCommunication = vaultCommunication;
}
@Override
public Health health() {
Health.Builder healthBuilder = Health.down();
if (this.vaultCommunication.isHealthy()) {
healthBuilder.up();
}
return healthBuilder.build();
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/VaultSecret.java
================================================
package de.koudingspawn.vault.vault;
import java.util.Map;
public class VaultSecret {
private Map<String, String> data;
private String compare;
private String type;
public VaultSecret(Map<String, String> data, String compare) {
this.data = data;
this.compare = compare;
this.type = "Opaque";
}
public VaultSecret(Map<String, String> data, String compare, String type) {
this.data = data;
this.compare = compare;
this.type = type;
}
public Map<String, String> getData() {
return data;
}
public void setData(Map<String, String> data) {
this.data = data;
}
public String getCompare() {
return compare;
}
public void setCompare(String compare) {
this.compare = compare;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/VaultService.java
================================================
package de.koudingspawn.vault.vault;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import org.springframework.stereotype.Component;
@Component
public class VaultService {
private final TypedSecretGeneratorFactory typedSecretGeneratorFactory;
public VaultService(TypedSecretGeneratorFactory typedSecretGeneratorFactory) {
this.typedSecretGeneratorFactory = typedSecretGeneratorFactory;
}
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
TypedSecretGenerator typedSecretGenerator = typedSecretGeneratorFactory.get(resource.getSpec().getType().toString() + "GENERATOR");
return typedSecretGenerator.generateSecret(resource);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/communication/SecretNotAccessibleException.java
================================================
package de.koudingspawn.vault.vault.communication;
public class SecretNotAccessibleException extends Exception {
public SecretNotAccessibleException(String message) {
super(message);
}
public SecretNotAccessibleException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/communication/TokenLookup.java
================================================
package de.koudingspawn.vault.vault.communication;
public class TokenLookup {
private String request_id;
private boolean renewable;
private TokenLookupData data;
public String getRequest_id() {
return request_id;
}
public void setRequest_id(String request_id) {
this.request_id = request_id;
}
public boolean isRenewable() {
return renewable;
}
public void setRenewable(boolean renewable) {
this.renewable = renewable;
}
public TokenLookupData getData() {
return data;
}
public void setData(TokenLookupData data) {
this.data = data;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/communication/TokenLookupData.java
================================================
package de.koudingspawn.vault.vault.communication;
public class TokenLookupData {
private String accessor;
private String display_name;
private String id;
private String path;
public String getAccessor() {
return accessor;
}
public void setAccessor(String accessor) {
this.accessor = accessor;
}
public String getDisplay_name() {
return display_name;
}
public void setDisplay_name(String display_name) {
this.display_name = display_name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/CertGenerator.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.impl.pki.PKIResponse;
import de.koudingspawn.vault.vault.impl.pki.VaultResponseData;
import org.springframework.stereotype.Component;
@Component("CERTGENERATOR")
public class CertGenerator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
private final SharedVaultResponseMapper sharedVaultResponseMapper;
public CertGenerator(VaultCommunication vaultCommunication, SharedVaultResponseMapper sharedVaultResponseMapper) {
this.vaultCommunication = vaultCommunication;
this.sharedVaultResponseMapper = sharedVaultResponseMapper;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
PKIResponse pkiResponse = vaultCommunication.getCert(resource.getSpec().getPath());
return sharedVaultResponseMapper.mapCert(pkiResponse.getData());
}
@Override
public String getHash(VaultSpec spec) throws SecretNotAccessibleException {
PKIResponse cert = vaultCommunication.getCert(spec.getPath());
if (cert != null && cert.getData() != null) {
VaultResponseData pkiResponse = cert.getData();
return sharedVaultResponseMapper.mapCert(pkiResponse).getCompare();
}
throw new SecretNotAccessibleException("Secret has no data field");
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/CertJksGenerator.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.impl.pki.PKIResponse;
import org.springframework.stereotype.Component;
@Component("CERTJKSGENERATOR")
public class CertJksGenerator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
private final SharedVaultResponseMapper sharedVaultResponseMapper;
public CertJksGenerator(VaultCommunication vaultCommunication, SharedVaultResponseMapper sharedVaultResponseMapper) {
this.vaultCommunication = vaultCommunication;
this.sharedVaultResponseMapper = sharedVaultResponseMapper;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
PKIResponse jksCert = vaultCommunication.getCert(resource.getSpec().getPath());
return sharedVaultResponseMapper.mapJks(jksCert.getData(), resource.getSpec().getJksConfiguration(), resource.getSpec().getType());
}
@Override
public String getHash(VaultSpec spec) {
return null;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/DockerCfgGenerator.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultDockerCfgConfiguration;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.impl.dockercfg.PullSecret;
import org.springframework.stereotype.Component;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Component("DOCKERCFGGENERATOR")
public class DockerCfgGenerator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
public DockerCfgGenerator(VaultCommunication vaultCommunication) {
this.vaultCommunication = vaultCommunication;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
PullSecret dockerCfg = vaultCommunication.getDockerCfg(resource.getSpec().getPath(), getConfiguration(resource.getSpec()));
return mapDockerCfg(dockerCfg);
}
@Override
public String getHash(VaultSpec spec) throws SecretNotAccessibleException {
PullSecret dockerCfg = vaultCommunication.getDockerCfg(spec.getPath(), getConfiguration(spec));
if (dockerCfg != null) {
return mapDockerCfg(dockerCfg).getCompare();
}
throw new SecretNotAccessibleException("Secret has no data field");
}
private VaultDockerCfgConfiguration getConfiguration(VaultSpec spec) {
return Optional.ofNullable(spec.getDockerCfgConfiguration()).orElse(new VaultDockerCfgConfiguration());
}
private VaultSecret mapDockerCfg(PullSecret pullSecret) {
String dockerCfg = String.format("{\"%s\": {\"username\": \"%s\", \"password\": \"%s\", \"email\": \"%s\", \"auth\": \"%s\"}}",
pullSecret.getUrl(), pullSecret.getUsername(), pullSecret.getPassword(), pullSecret.getEmail(), pullSecret.getAuth());
Map<String, String> data = new HashMap<>();
data.put(".dockercfg", Base64.getEncoder().encodeToString(dockerCfg.getBytes()));
return new VaultSecret(data, Sha256.generateSha256(dockerCfg), "kubernetes.io/dockercfg");
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/EncryptionUtils.java
================================================
package de.koudingspawn.vault.vault.impl;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
// https://github.com/Mastercard/client-encryption-java/blob/44b38fbc8e9fd64d252cbbf47a4bc5208a8ae741/src/main/java/com/mastercard/developer/utils/EncryptionUtils.java#L95
public class EncryptionUtils {
private static final String PKCS_1_PEM_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
private static final String PKCS_1_PEM_FOOTER = "-----END RSA PRIVATE KEY-----";
private static final String PKCS_8_PEM_HEADER = "-----BEGIN PRIVATE KEY-----";
private static final String PKCS_8_PEM_FOOTER = "-----END PRIVATE KEY-----";
private EncryptionUtils() {
}
/**
* Load a RSA decryption key from a file (PEM or DER).
*/
public static PrivateKey loadPrivateKey(String keyDataString) throws GeneralSecurityException {
if (keyDataString.contains(PKCS_1_PEM_HEADER)) {
// OpenSSL / PKCS#1 Base64 PEM encoded file
keyDataString = keyDataString.replace(PKCS_1_PEM_HEADER, "");
keyDataString = keyDataString.replace(PKCS_1_PEM_FOOTER, "");
keyDataString = keyDataString.replace("\n", "");
keyDataString = keyDataString.replace("\r\n", "");
return readPkcs1PrivateKey(Base64.getDecoder().decode(keyDataString));
}
if (keyDataString.contains(PKCS_8_PEM_HEADER)) {
// PKCS#8 Base64 PEM encoded file
keyDataString = keyDataString.replace(PKCS_8_PEM_HEADER, "");
keyDataString = keyDataString.replace(PKCS_8_PEM_FOOTER, "");
keyDataString = keyDataString.replace("\n", "");
keyDataString = keyDataString.replace("\r\n", "");
return readPkcs8PrivateKey(Base64.getDecoder().decode(keyDataString));
}
throw new GeneralSecurityException("No parser for secret found");
}
/**
* Create a PrivateKey instance from raw PKCS#8 bytes.
*/
private static PrivateKey readPkcs8PrivateKey(byte[] pkcs8Bytes) throws GeneralSecurityException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Bytes);
try {
return keyFactory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
throw new IllegalArgumentException("Unexpected key format!", e);
}
}
/**
* Create a PrivateKey instance from raw PKCS#1 bytes.
*/
private static PrivateKey readPkcs1PrivateKey(byte[] pkcs1Bytes) throws GeneralSecurityException {
// We can't use Java internal APIs to parse ASN.1 structures, so we build a PKCS#8 key Java can understand
int pkcs1Length = pkcs1Bytes.length;
int totalLength = pkcs1Length + 22;
byte[] pkcs8Header = new byte[]{
0x30, (byte) 0x82, (byte) ((totalLength >> 8) & 0xff), (byte) (totalLength & 0xff), // Sequence + total length
0x2, 0x1, 0x0, // Integer (0)
0x30, 0xD, 0x6, 0x9, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0, // Sequence: 1.2.840.113549.1.1.1, NULL
0x4, (byte) 0x82, (byte) ((pkcs1Length >> 8) & 0xff), (byte) (pkcs1Length & 0xff) // Octet string + length
};
byte[] pkcs8bytes = join(pkcs8Header, pkcs1Bytes);
return readPkcs8PrivateKey(pkcs8bytes);
}
private static byte[] join(byte[] byteArray1, byte[] byteArray2) {
byte[] bytes = new byte[byteArray1.length + byteArray2.length];
System.arraycopy(byteArray1, 0, bytes, 0, byteArray1.length);
System.arraycopy(byteArray2, 0, bytes, byteArray1.length, byteArray2.length);
return bytes;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/KeyValueGenerator.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import org.springframework.stereotype.Component;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
@Component("KEYVALUEGENERATOR")
public class KeyValueGenerator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
public KeyValueGenerator(VaultCommunication vaultCommunication) {
this.vaultCommunication = vaultCommunication;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
HashMap keyValueResponse = vaultCommunication.getKeyValue(resource.getSpec().getPath());
return mapKeyValueResponse(keyValueResponse);
}
@Override
public String getHash(VaultSpec resource) throws SecretNotAccessibleException {
HashMap keyValue = vaultCommunication.getKeyValue(resource.getPath());
if (keyValue != null) {
return mapKeyValueResponse(keyValue).getCompare();
}
throw new SecretNotAccessibleException("Secret has no data field");
}
private VaultSecret mapKeyValueResponse(HashMap<String, String> keyValue) {
TreeMap<String, String> sortedMap = new TreeMap<>(keyValue);
Map<String, String> base64Encoded = sortedMap.entrySet()
.stream()
.collect(Collectors
.toMap(Map.Entry::getKey,
e -> Base64.getEncoder().encodeToString(e.getValue().getBytes())));
String sha256 = Sha256.generateSha256(base64Encoded.values().toArray(new String[0]));
return new VaultSecret(base64Encoded, sha256);
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/KeyValueV2Generator.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component("KEYVALUEV2GENERATOR")
public class KeyValueV2Generator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
public KeyValueV2Generator(VaultCommunication vaultCommunication) {
this.vaultCommunication = vaultCommunication;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
Optional<Integer> version = getVersion(resource.getSpec());
HashMap versionedKVResponse =
vaultCommunication.getVersionedSecret(resource.getSpec().getPath(), version);
return mapKeyValueResponse(versionedKVResponse);
}
@Override
public String getHash(VaultSpec resource) throws SecretNotAccessibleException {
Optional<Integer> version = getVersion(resource);
HashMap keyValue = vaultCommunication.getVersionedSecret(resource.getPath(), version);
if (keyValue != null) {
return mapKeyValueResponse(keyValue).getCompare();
}
throw new SecretNotAccessibleException("Secret has no data field");
}
private VaultSecret mapKeyValueResponse(HashMap<String, String> keyValue) {
TreeMap<String, String> sortedMap = new TreeMap<>(keyValue);
Map<String, String> base64Encoded = sortedMap.entrySet()
.stream()
.collect(Collectors
.toMap(Map.Entry::getKey,
e -> Base64.getEncoder().encodeToString(e.getValue().getBytes())));
String sha256 = Sha256.generateSha256(base64Encoded.values().toArray(new String[0]));
return new VaultSecret(base64Encoded, sha256);
}
private Optional<Integer> getVersion(VaultSpec resource) {
if (resource.getVersionConfiguration() != null ) {
return Optional.ofNullable(resource.getVersionConfiguration().getVersion());
}
return Optional.empty();
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/PkiJksGenerator.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.impl.pki.PKIResponse;
import org.springframework.stereotype.Component;
@Component("PKIJKSGENERATOR")
public class PkiJksGenerator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
private final SharedVaultResponseMapper sharedVaultResponseMapper;
public PkiJksGenerator(VaultCommunication vaultCommunication, SharedVaultResponseMapper sharedVaultResponseMapper) {
this.vaultCommunication = vaultCommunication;
this.sharedVaultResponseMapper = sharedVaultResponseMapper;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
PKIResponse jksPki = vaultCommunication.createPki(resource.getSpec().getPath(), resource.getSpec().getPkiConfiguration());
return sharedVaultResponseMapper.mapJks(jksPki.getData(), resource.getSpec().getJksConfiguration(), resource.getSpec().getType());
}
@Override
public String getHash(VaultSpec spec) {
return null;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/PkiSecretGenerator.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.impl.pki.PKIResponse;
import org.springframework.stereotype.Component;
@Component("PKIGENERATOR")
public class PkiSecretGenerator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
private final SharedVaultResponseMapper sharedVaultResponseMapper;
public PkiSecretGenerator(VaultCommunication vaultCommunication, SharedVaultResponseMapper sharedVaultResponseMapper) {
this.vaultCommunication = vaultCommunication;
this.sharedVaultResponseMapper = sharedVaultResponseMapper;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
PKIResponse pki = vaultCommunication.createPki(resource.getSpec().getPath(), resource.getSpec().getPkiConfiguration());
return sharedVaultResponseMapper.mapPki(pki.getData());
}
@Override
public String getHash(VaultSpec spec) {
return null;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/PropertiesGenerator.java
================================================
package de.koudingspawn.vault.vault.impl;
import com.google.common.collect.Maps;
import com.hubspot.jinjava.Jinjava;
import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultPropertiesConfiguration;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.vault.TypedSecretGenerator;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.impl.properties.VaultJinjaLookup;
import org.springframework.stereotype.Component;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Component("PROPERTIESGENERATOR")
public class PropertiesGenerator implements TypedSecretGenerator {
private final VaultCommunication vaultCommunication;
public PropertiesGenerator(VaultCommunication vaultCommunication) {
this.vaultCommunication = vaultCommunication;
}
@Override
public VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleException {
VaultPropertiesConfiguration propertiesConfiguration = resource.getSpec().getPropertiesConfiguration();
if (propertiesConfiguration != null && propertiesConfiguration.getFiles() != null) {
Map<String, Object> context = Maps.newHashMap();
context.put("vault", new VaultJinjaLookup(vaultCommunication));
if (propertiesConfiguration.getContext() != null) {
context.putAll(propertiesConfiguration.getContext());
}
try {
Map<String, String> renderedFiles = renderFiles(context, propertiesConfiguration.getFiles());
// TODO: support change in properties
return new VaultSecret(renderedFiles, "COMPARE");
} catch (FatalTemplateErrorsException ex) {
throw new SecretNotAccessibleException(ex.getMessage(), ex);
}
}
throw new SecretNotAccessibleException("Does not contain the required Files to render");
}
@Override
public String getHash(VaultSpec spec) throws SecretNotAccessibleException {
return "COMPARE";
}
private Map<String, String> renderFiles(Map<String, Object> context, Map<String, String> files) throws FatalTemplateErrorsException {
Jinjava jinjava = new Jinjava();
Map<String, String> targetFiles = new HashMap<>();
files.forEach((key, value) -> {
String renderedContent = jinjava.render(value, context);
targetFiles.put(key, Base64.getEncoder().encodeToString(renderedContent.getBytes()));
});
return targetFiles;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/Sha256.java
================================================
package de.koudingspawn.vault.vault.impl;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Sha256 {
public static String generateSha256(String ... args) {
try {
StringBuilder sb = new StringBuilder();
for (String arg : args) {
sb.append(arg).append(";");
}
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/SharedVaultResponseMapper.java
================================================
package de.koudingspawn.vault.vault.impl;
import de.koudingspawn.vault.Constants;
import de.koudingspawn.vault.crd.VaultJKSConfiguration;
import de.koudingspawn.vault.crd.VaultType;
import de.koudingspawn.vault.vault.VaultSecret;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import de.koudingspawn.vault.vault.impl.pki.VaultResponseData;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
@Component
public class SharedVaultResponseMapper {
@Value("${kubernetes.jks.default-alias}")
private String defaultAlias;
@Value("${kubernetes.jks.default-password}")
private String defaultPassword;
@Value("${kubernetes.jks.default-secret-key-name}")
private String defaultKeyName;
VaultSecret mapPki(VaultResponseData responseData) throws SecretNotAccessibleException {
try {
Certificate[] publicKeyList = getPublicKey(responseData.getCertificate());
X509Certificate compareCert = getCertificateWithShortestLivetime(publicKeyList);
SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.DATE_FORMAT);
TimeZone tz = TimeZone.getTimeZone("UTC");
dateFormat.setTimeZone(tz);
String compare = dateFormat.format(compareCert.getNotAfter());
Map<String, String> mappedPki = mappedCertValues(responseData);
return new VaultSecret(mappedPki, compare);
} catch (CertificateException e) {
throw new SecretNotAccessibleException("Couldn't get Expiration date of pki", e);
}
}
VaultSecret mapCert(VaultResponseData vaultResponseData) {
Map<String, String> mappedPki = mappedCertValues(vaultResponseData);
String compareSha = Sha256.generateSha256(
mappedPki.get("tls.crt"),
mappedPki.get("tls.key")
);
return new VaultSecret(mappedPki, compareSha);
}
private Map<String, String> mappedCertValues(VaultResponseData vaultResponseData) {
String crt = getCrt(vaultResponseData);
String key = getKey(vaultResponseData);
Map<String, String> mappedPki = new HashMap<>();
mappedPki.put("tls.crt", crt);
mappedPki.put("tls.key", key);
return mappedPki;
}
private String getCrt(VaultResponseData responseData) {
return Base64.getEncoder().encodeToString(responseData.getChainedCertificate().getBytes());
}
private String getKey(VaultResponseData responseData) {
return Base64.getEncoder().encodeToString(responseData.getPrivate_key().getBytes());
}
VaultSecret mapJks(VaultResponseData data, VaultJKSConfiguration jksConfiguration, VaultType type) throws SecretNotAccessibleException {
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
Certificate[] publicKeyList = getPublicKey(data.getCertificate());
keyStore.setKeyEntry(
getAlias(jksConfiguration),
EncryptionUtils.loadPrivateKey(data.getPrivate_key()),
getPassword(jksConfiguration).toCharArray(),
publicKeyList);
if (jksConfiguration != null && StringUtils.hasText(jksConfiguration.getCaAlias())) {
keyStore.setCertificateEntry(
jksConfiguration.getCaAlias(),
getPublicKey(data.getIssuing_ca())[0]
);
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
keyStore.store(outputStream, getPassword(jksConfiguration).toCharArray());
String b64KeyStore = Base64.getEncoder().encodeToString(outputStream.toByteArray());
HashMap<String, String> secretData = new HashMap<>() {{
put(getKey(jksConfiguration), b64KeyStore);
}};
String compare;
if (type.equals(VaultType.CERTJKS)) {
String base64Cert = Base64.getEncoder().encodeToString(data.getCertificate().getBytes());
String base64Key = Base64.getEncoder().encodeToString(data.getPrivate_key().getBytes());
compare = Sha256.generateSha256(base64Cert, base64Key);
} else {
// VaultType.PKIJKS
X509Certificate compareCert = getCertificateWithShortestLivetime(publicKeyList);
SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.DATE_FORMAT);
TimeZone tz = TimeZone.getTimeZone("UTC");
dateFormat.setTimeZone(tz);
compare = dateFormat.format(compareCert.getNotAfter());
}
return new VaultSecret(secretData, compare);
} catch (IOException | GeneralSecurityException e) {
throw new SecretNotAccessibleException("Couldn't generate keystore", e);
}
}
private String getAlias(VaultJKSConfiguration jksConfiguration) {
if (jksConfiguration == null || !StringUtils.hasText(jksConfiguration.getAlias())) {
return defaultAlias;
}
return jksConfiguration.getAlias();
}
private String getPassword(VaultJKSConfiguration jksConfiguration) {
if (jksConfiguration == null || !StringUtils.hasText(jksConfiguration.getPassword())) {
return defaultPassword;
}
return jksConfiguration.getPassword();
}
private String getKey(VaultJKSConfiguration jksConfiguration) {
if (jksConfiguration == null || !StringUtils.hasText(jksConfiguration.getKeyName())) {
return defaultKeyName;
}
return jksConfiguration.getKeyName();
}
private Certificate[] getPublicKey(String pem) throws CertificateException {
return CertificateFactory.getInstance("X509")
.generateCertificates(new ByteArrayInputStream(pem.getBytes())).toArray(new Certificate[0]);
}
private X509Certificate getCertificateWithShortestLivetime(Certificate[] certificates) {
if (certificates.length == 1) {
return (X509Certificate) certificates[0];
} else {
X509Certificate shortestLiveTime = (X509Certificate) certificates[0];
for (Certificate certificate : certificates) {
if (((X509Certificate) certificate).getNotAfter().before(shortestLiveTime.getNotAfter())) {
shortestLiveTime = (X509Certificate) certificate;
}
}
return shortestLiveTime;
}
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/dockercfg/PullSecret.java
================================================
package de.koudingspawn.vault.vault.impl.dockercfg;
import java.util.Base64;
public class PullSecret {
private String username;
private String password;
private String email;
private String url;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getAuth() {
String concatedAuth = getUsername() + ":" + getPassword();
return Base64.getEncoder().encodeToString(concatedAuth.getBytes());
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/pki/PKIRequest.java
================================================
package de.koudingspawn.vault.vault.impl.pki;
public class PKIRequest {
private String common_name;
private String alt_names;
private String ip_sans;
private String format = "pem";
private String ttl;
public String getCommon_name() {
return common_name;
}
public void setCommon_name(String common_name) {
this.common_name = common_name;
}
public String getAlt_names() {
return alt_names;
}
public void setAlt_names(String alt_names) {
this.alt_names = alt_names;
}
public String getIp_sans() {
return ip_sans;
}
public void setIp_sans(String ip_sans) {
this.ip_sans = ip_sans;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public String getTtl() {
return ttl;
}
public void setTtl(String ttl) {
this.ttl = ttl;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/pki/PKIResponse.java
================================================
package de.koudingspawn.vault.vault.impl.pki;
public class PKIResponse {
private String lease_id;
private boolean renewable;
private VaultResponseData data;
public String getLease_id() {
return lease_id;
}
public void setLease_id(String lease_id) {
this.lease_id = lease_id;
}
public boolean isRenewable() {
return renewable;
}
public void setRenewable(boolean renewable) {
this.renewable = renewable;
}
public VaultResponseData getData() {
return data;
}
public void setData(VaultResponseData data) {
this.data = data;
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/pki/VaultResponseData.java
================================================
package de.koudingspawn.vault.vault.impl.pki;
import org.springframework.util.CollectionUtils;
import java.util.List;
public class VaultResponseData {
private String certificate;
private String issuing_ca;
private List<String> ca_chain;
private String private_key;
private String private_key_type;
private String serial_number;
public String getCertificate() {
return certificate;
}
public void setCertificate(String certificate) {
this.certificate = certificate;
}
public String getIssuing_ca() {
return issuing_ca;
}
public void setIssuing_ca(String issuing_ca) {
this.issuing_ca = issuing_ca;
}
public List<String> getCa_chain() {
return ca_chain;
}
public void setCa_chain(List<String> ca_chain) {
this.ca_chain = ca_chain;
}
public String getPrivate_key() {
return private_key;
}
public void setPrivate_key(String private_key) {
this.private_key = private_key;
}
public String getPrivate_key_type() {
return private_key_type;
}
public void setPrivate_key_type(String private_key_type) {
this.private_key_type = private_key_type;
}
public String getSerial_number() {
return serial_number;
}
public void setSerial_number(String serial_number) {
this.serial_number = serial_number;
}
public String getChainedCertificate() {
StringBuilder sb = new StringBuilder(certificate);
if (!CollectionUtils.isEmpty(ca_chain)) {
ca_chain.forEach(cert -> sb.append("\n").append(cert));
}
return sb.toString();
}
}
================================================
FILE: src/main/java/de/koudingspawn/vault/vault/impl/properties/VaultJinjaLookup.java
================================================
package de.koudingspawn.vault.vault.impl.properties;
import de.koudingspawn.vault.vault.VaultCommunication;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import java.util.HashMap;
import java.util.Optional;
public class VaultJinjaLookup {
private final VaultCommunication vaultCommunication;
public VaultJinjaLookup(VaultCommunication vaultCommunication) {
this.vaultCommunication = vaultCommunication;
}
public String lookup(String path, String key) throws SecretNotAccessibleException {
return vaultCommunication.getKeyValue(path).get(key).toString();
}
public HashMap lookup(String path) throws SecretNotAccessibleException {
return vaultCommunication.getKeyValue(path);
}
public HashMap lookupV2(String path) throws SecretNotAccessibleException {
return vaultCommunication.getVersionedSecret(path, Optional.empty());
}
public String lookupV2(String path, String key) throws SecretNotAccessibleException {
HashMap versionedSecret = vaultCommunication.getVersionedSecret(path, Optional.empty());
if (versionedSecret.containsKey(key)) {
return versionedSecret.get(key).toString();
}
throw new SecretNotAccessibleException(String.format("Secret at path %s with key %s not available", path, key));
}
public String lookupV2(String path, int version, String key) throws SecretNotAccessibleException {
HashMap versionedSecret = vaultCommunication.getVersionedSecret(path, Optional.of(version));
if (versionedSecret.containsKey(key)) {
return versionedSecret.get(key).toString();
}
throw new SecretNotAccessibleException(String.format("Secret at path %s in version %d with key %s not available", path, version, key));
}
}
================================================
FILE: src/main/resources/application.properties
================================================
kubernetes.crd.group=koudingspawn.de
kubernetes.crd.name=vault.koudingspawn.de
kubernetes.vault.auth=token
kubernetes.vault.token=root
kubernetes.vault.role=admin
kubernetes.vault.url=http://localhost:8200/v1/
kubernetes.interval=60
kubernetes.jks.default-alias=main
kubernetes.jks.default-password=changeit
kubernetes.jks.default-secret-key-name=key.jks
kubernetes.ownerreference-fix.enabled=false
management.endpoints.enabled-by-default=false
management.endpoint.health.enabled=true
================================================
FILE: src/test/java/de/koudingspawn/vault/CertChainTest.java
================================================
package de.koudingspawn.vault;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.crd.VaultType;
import de.koudingspawn.vault.kubernetes.EventHandler;
import de.koudingspawn.vault.kubernetes.scheduler.impl.CertRefresh;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.UUID;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static de.koudingspawn.vault.Constants.COMPARE_ANNOTATION;
import static de.koudingspawn.vault.Constants.LAST_UPDATE_ANNOTATION;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest(
properties = {
"kubernetes.vault.url=http://localhost:8206/v1/",
"kubernetes.vault.token=c73ab0cb-41e6-b89c-7af6-96b36f1ac87b"
}
)
public class CertChainTest {
@ClassRule
public static final WireMockClassRule wireMockClassRule =
new WireMockClassRule(wireMockConfig().port(8206));
@Rule
public WireMockClassRule instanceRule = wireMockClassRule;
@Autowired
public EventHandler handler;
@Autowired
public KubernetesClient client;
@org.springframework.boot.test.context.TestConfiguration
static class KindConfig {
@Bean
@Primary
public KubernetesClient client() {
return new KubernetesClientBuilder().build();
}
}
@Autowired
CertRefresh certRefresh;
@Before
public void before() {
WireMock.resetAllScenarios();
client.secrets().inAnyNamespace().delete();
TestHelper.generateLookupSelfStub();
}
@Test
public void shouldGenerateCertFromVaultResource() {
Vault vault = new Vault();
vault.setMetadata(
new ObjectMetaBuilder().withName("certificate-1").withNamespace("default").withUid(UUID.randomUUID().toString()).build()
);
VaultSpec spec = new VaultSpec();
spec.setType(VaultType.CERT);
spec.setPath("secret/certificate");
vault.setSpec(spec);
stubFor(get(urlEqualTo("/v1/secret/certificate"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("""
{
"request_id": "6cc090a8-3821-8244-73e4-5ab62b605587",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"data": {
"certificate": "CERTIFICATE",
"issuing_ca": "ISSUINGCA",
"ca_chain": ["ISSUINGCA"],
"private_key": "PRIVATEKEY"
}
},
"wrap_info": null,
"warnings": null,
"auth": null
}""")));
handler.addHandler(vault);
Secret secret = client.secrets().inNamespace("default").withName("certificate-1").get();
assertEquals("certificate-1", secret.getMetadata().getName());
assertEquals("default", secret.getMetadata().getNamespace());
assertEquals("Opaque", secret.getType());
assertNotNull(secret.getMetadata().getAnnotations().get("vault.koudingspawn.de" + LAST_UPDATE_ANNOTATION));
assertEquals("GwzyEg3PQ2uSYFL2U6i0X2RibVs9p5gvOoTdZVQdT6s=", secret.getMetadata().getAnnotations().get("vault.koudingspawn.de" + COMPARE_ANNOTATION));
String crtB64 = secret.getData().get("tls.crt");
String crt = new String(java.util.Base64.getDecoder().decode(crtB64));
String keyB64 = secret.getData().get("tls.key");
String key = new String(java.util.Base64.getDecoder().decode(keyB64));
assertEquals("CERTIFICATE\nISSUINGCA", crt);
assertEquals("PRIVATEKEY", key);
}
@Test
public void shouldCheckIfCertificateHasChangedAndReturnFalse() throws SecretNotAccessibleException {
Vault vault = new Vault();
vault.setMetadata(
new ObjectMetaBuilder().withName("certificate-2").withNamespace("default").withUid(UUID.randomUUID().toString()).build()
);
VaultSpec spec = new VaultSpec();
spec.setType(VaultType.CERT);
spec.setPath("secret/certificate");
vault.setSpec(spec);
stubFor(get(urlEqualTo("/v1/secret/certificate"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("""
{
"request_id": "6cc090a8-3821-8244-73e4-5ab62b605587",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"data": {
"certificate": "CERTIFICATE",
"issuing_ca": "ISSUINGCA",
"ca_chain": ["ISSUINGCA"],
"private_key": "PRIVATEKEY"
}
},
"wrap_info": null,
"warnings": null,
"auth": null
}""")));
handler.addHandler(vault);
assertFalse(certRefresh.refreshIsNeeded(vault));
}
@Test
public void shouldCheckIfCertificateHasChangedAndReturnTrue() throws SecretNotAccessibleException {
Vault vault = new Vault();
vault.setMetadata(
new ObjectMetaBuilder().withName("certificate-3").withNamespace("default").withUid(UUID.randomUUID().toString()).build()
);
VaultSpec spec = new VaultSpec();
spec.setType(VaultType.CERT);
spec.setPath("secret/certificate");
vault.setSpec(spec);
stubFor(get(urlEqualTo("/v1/secret/certificate"))
.inScenario("Cert secret change")
.whenScenarioStateIs(STARTED)
.willSetStateTo("Cert first request done")
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("""
{
"request_id": "6cc090a8-3821-8244-73e4-5ab62b605587",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"data": {
"certificate": "CERTIFICATE",
"issuing_ca": "ISSUINGCA",
"ca_chain": ["ISSUINGCA"],
"private_key": "PRIVATEKEY"
}
},
"wrap_info": null,
"warnings": null,
"auth": null
}""")));
stubFor(get(urlEqualTo("/v1/secret/certificate"))
.inScenario("Cert secret change")
.whenScenarioStateIs("Cert first request done")
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("""
{
"request_id": "6cc090a8-3821-8244-73e4-5ab62b605587",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"data": {
"certificate": "CERTIFICATECHANGE",
"issuing_ca": "ISSUINGCA",
"ca_chain": ["ISSUINGCA"],
"private_key": "PRIVATEKEY"
}
},
"wrap_info": null,
"warnings": null,
"auth": null
}""")));
handler.addHandler(vault);
assertTrue(certRefresh.refreshIsNeeded(vault));
}
}
================================================
FILE: src/test/java/de/koudingspawn/vault/CertTest.java
================================================
package de.koudingspawn.vault;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import de.koudingspawn.vault.crd.Vault;
import de.koudingspawn.vault.crd.VaultSpec;
import de.koudingspawn.vault.crd.VaultType;
import de.koudingspawn.vault.kubernetes.EventHandler;
import de.koudingspawn.vault.kubernetes.scheduler.impl.CertRefresh;
import de.koudingspawn.vault.vault.communication.SecretNotAccessibleException;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.UUID;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static de.koudingspawn.vault.Constants.COMPARE_ANNOTATION;
import static de.koudingspawn.vault.Constants.LAST_UPDATE_ANNOTATION;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest(
properties = {
"kubernetes.vault.url=http://localhost:8201/v1/"
}
)
public class CertTest {
@ClassRule
public static final WireMockClassRule wireMockClassRule =
new WireMockClassRule(wireMockConfig().port(8201));
@Rule
public WireMockClassRule instanceRule = wireMockClassRule;
@Autowired
public EventHandler handler;
@Autowired
public KubernetesClient client;
@org.springframework.boot.test.context.TestConfiguration
static class KindConfig {
@Bean
@Primary
public KubernetesClient client() {
return new KubernetesClientBuilder().build();
}
}
@Autowired
CertRefresh certRefresh;
@Before
public void before() {
WireMock.resetAllScenarios();
client.secrets().inAnyNamespace().delete();
TestHelper.generateLookupSelfStub();
}
@Test
public void shouldGenerateCertFromVaultResource() {
Vault vault = new Vault();
vault.setMetadata(
new ObjectMetaBuilder().withName("certificate-1").withNamespace("default").withUid(UUID.randomUUID().toString()).build()
);
VaultSpec spec = new VaultSpec();
spec.setType(VaultType.CERT);
spec.setPath("secret/certificate");
vault.setSpec(spec);
stubFor(get(urlEqualTo("/v1/secret/certificate"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("""
{
"request_id": "6cc090a8-3821-8244-73e4-5ab62b605587",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"data": {
"certificate": "CERTIFICATE",
"issuing_ca": "ISSUINGCA",
"private_key": "PRIVATEKEY"
}
},
"wrap_info": null,
"warnings": null,
"auth": null
}""")));
handler.addHandler(vault);
Secret secret = client.secrets().inNamespace("default").withName("certificate-1").get();
assertEquals("certificate-1", secret.getMetadata().getName());
assertEquals("default", secret.getMetadata().getNamespace());
assertEquals("Opaque", secret.getType());
assertNotNull(secret.getMetadata().getAnnotations().get("vault.koudingspawn.de" + LAST_UPDATE_ANNOTATION));
assertEquals("NNreOhDpdqcmcxEvF/KGNSQBZpAjszzrhjQVT4X8EXE=", secret.getMetadata().getAnnotations().get("vault.koudingspawn.de" + COMPARE_ANNOTATION));
String crtB64 = secret.getData().get("tls.crt");
String crt = new String(java.util.Base64.getDecoder().decode(crtB64));
String keyB64 = secret.getData().get("tls.key");
String key = new String(java.util.Base64.getDecoder().decode(keyB64));
assertEquals("CERTIFICATE", crt);
assertEquals("PRIVATEKEY", key);
}
@Test
public void shouldCheckIfCertificateHasChangedAndReturnFalse() throws SecretNotAccessibleException {
Vault vault = new Vault();
vault.setMetadata(
new ObjectMetaBuilder().withName("certificate-2").withNamespace("default").withUid(UUID.randomUUID().toString()).build()
);
VaultSpec spec = new VaultSpec();
spec.setType(VaultType.CERT);
spec.setPath("secret/certificate");
vault.setSpec(spec);
stubFor(get(urlEqualTo("/v1/secret/certificate"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("""
{
"request_id": "6cc090a8-3821-8244-73e4-5ab62b605587",
"lease_id": "",
"renewable": false,
"lease_duration": 2764800,
"data": {
"data": {
"certificate": "CERTIFICATE",
"issuing_ca": "ISSUINGCA",
"private_key": "PRIVATEKEY"
}
},
"wrap_info": null,
"warnings": null,
"auth": null
}""")));
handler.addHandler(vault);
assertFalse(certRefresh.refreshIsNeeded(vault));
}
@Test
public void shouldCheckIfCertificateHasChangedAndReturnTrue() throws SecretNotAccessibleException {
Vault vault = new Vault();
vault.setMetadata(
new ObjectMetaBuilder().withName("certificate-3").withNamespace("default").withUid(UUID.randomUUID().toString()).build()
);
VaultSpec spec = new VaultSpec();
spec.setType(VaultType.CERT);
spec.setPath("secret/certificate");
vault.setSpec(spec);
stubFor(get(urlEqualTo("/v1/secret/certificate"))
.inScenario("Cert secret change")
.whenScenarioStateIs(STARTED)
.willSetStateTo("Cert first request done")
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("""
{
"request_id": "6cc090a8-3821-8244-73e4-5ab62b605587",
gitextract_p7tbtk3k/
├── .github/
│ └── workflows/
│ ├── codeql-analysis.yml
│ └── maven.yaml
├── .gitignore
├── .mvn/
│ └── wrapper/
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── Dockerfile
├── LICENSE
├── crd.yml
├── deploy/
│ ├── admission-webhook.yaml
│ └── rbac.yaml
├── examples/
│ ├── cert.yml
│ ├── certjks-rollout-redo.yml
│ ├── certjks.yml
│ ├── dockercfg-error.yml
│ ├── dockercfg.yml
│ ├── keyvalue.yml
│ ├── keyvaluev2-version.yml
│ ├── keyvaluev2.yml
│ ├── kind/
│ │ ├── cluster.yaml
│ │ ├── run.sh
│ │ └── vault.yaml
│ ├── pki.yml
│ ├── pki_chain.yml
│ ├── pkijks.yml
│ └── properties.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── readme.md
└── src/
├── main/
│ ├── java/
│ │ └── de/
│ │ └── koudingspawn/
│ │ └── vault/
│ │ ├── Constants.java
│ │ ├── VaultApplication.java
│ │ ├── admissionreview/
│ │ │ ├── AdmissionReviewRestService.java
│ │ │ └── AdmissionReviewService.java
│ │ ├── crd/
│ │ │ ├── Vault.java
│ │ │ ├── VaultChangeAdjustmentCallback.java
│ │ │ ├── VaultDockerCfgConfiguration.java
│ │ │ ├── VaultJKSConfiguration.java
│ │ │ ├── VaultList.java
│ │ │ ├── VaultPkiConfiguration.java
│ │ │ ├── VaultPropertiesConfiguration.java
│ │ │ ├── VaultSpec.java
│ │ │ ├── VaultType.java
│ │ │ └── VaultVersionedConfiguration.java
│ │ ├── kubernetes/
│ │ │ ├── ChangeAdjustmentService.java
│ │ │ ├── EventHandler.java
│ │ │ ├── KubernetesConnection.java
│ │ │ ├── KubernetesService.java
│ │ │ ├── Watcher.java
│ │ │ ├── cache/
│ │ │ │ ├── SecretCache.java
│ │ │ │ └── SecretCacheConfiguration.java
│ │ │ ├── event/
│ │ │ │ ├── EventNotification.java
│ │ │ │ └── EventType.java
│ │ │ └── scheduler/
│ │ │ ├── RefreshConfiguration.java
│ │ │ ├── RequiresRefresh.java
│ │ │ ├── ScheduledRefresh.java
│ │ │ ├── TypeRefreshFactory.java
│ │ │ └── impl/
│ │ │ ├── CertJksRefresh.java
│ │ │ ├── CertRefresh.java
│ │ │ ├── CompareHash.java
│ │ │ ├── DockerCfgRefresh.java
│ │ │ ├── KeyValueRefresh.java
│ │ │ ├── KeyValueV2Refresh.java
│ │ │ ├── PkiJksRefresh.java
│ │ │ ├── PkiRefresh.java
│ │ │ └── PropertiesRefresh.java
│ │ └── vault/
│ │ ├── TypedSecretGenerator.java
│ │ ├── TypedSecretGeneratorFactory.java
│ │ ├── VaultCommunication.java
│ │ ├── VaultConfiguration.java
│ │ ├── VaultHealthCheck.java
│ │ ├── VaultSecret.java
│ │ ├── VaultService.java
│ │ ├── communication/
│ │ │ ├── SecretNotAccessibleException.java
│ │ │ ├── TokenLookup.java
│ │ │ └── TokenLookupData.java
│ │ └── impl/
│ │ ├── CertGenerator.java
│ │ ├── CertJksGenerator.java
│ │ ├── DockerCfgGenerator.java
│ │ ├── EncryptionUtils.java
│ │ ├── KeyValueGenerator.java
│ │ ├── KeyValueV2Generator.java
│ │ ├── PkiJksGenerator.java
│ │ ├── PkiSecretGenerator.java
│ │ ├── PropertiesGenerator.java
│ │ ├── Sha256.java
│ │ ├── SharedVaultResponseMapper.java
│ │ ├── dockercfg/
│ │ │ └── PullSecret.java
│ │ ├── pki/
│ │ │ ├── PKIRequest.java
│ │ │ ├── PKIResponse.java
│ │ │ └── VaultResponseData.java
│ │ └── properties/
│ │ └── VaultJinjaLookup.java
│ └── resources/
│ └── application.properties
└── test/
├── java/
│ └── de/
│ └── koudingspawn/
│ └── vault/
│ ├── CertChainTest.java
│ ├── CertTest.java
│ ├── DockerCfgTest.java
│ ├── EventNotificationTest.java
│ ├── KeyValueTest.java
│ ├── KeyValueV2Test.java
│ ├── OwnerReferenceBugfixTest.java
│ ├── PKIChainTest.java
│ ├── PKITest.java
│ ├── PropertiesTest.java
│ ├── TestHelper.java
│ ├── admissionreview/
│ │ └── AdmissionReviewTest.java
│ ├── kubernetes/
│ │ ├── EventHandlerTest.java
│ │ └── KubernetesServiceTest.java
│ └── vault/
│ └── VaultHealthCheckTest.java
└── resources/
├── application.properties
├── test.properties
└── vault-crd.yaml
SYMBOL INDEX (448 symbols across 77 files)
FILE: src/main/java/de/koudingspawn/vault/Constants.java
class Constants (line 3) | public class Constants {
method Constants (line 5) | private Constants() {
FILE: src/main/java/de/koudingspawn/vault/VaultApplication.java
class VaultApplication (line 7) | @SpringBootApplication
method main (line 11) | public static void main(String[] args) {
FILE: src/main/java/de/koudingspawn/vault/admissionreview/AdmissionReviewRestService.java
class AdmissionReviewRestService (line 11) | @RestController
method AdmissionReviewRestService (line 17) | public AdmissionReviewRestService(AdmissionReviewService admissionRevi...
method validate (line 21) | @PostMapping
FILE: src/main/java/de/koudingspawn/vault/admissionreview/AdmissionReviewService.java
class AdmissionReviewService (line 16) | @Service
method AdmissionReviewService (line 23) | public AdmissionReviewService(VaultService vaultService) {
method validate (line 27) | public AdmissionResponse validate(AdmissionRequest admissionRequest) {
method validRequest (line 43) | private AdmissionResponse validRequest(String uuid) {
method invalidRequest (line 50) | private AdmissionResponse invalidRequest(String uid, String message) {
FILE: src/main/java/de/koudingspawn/vault/crd/Vault.java
class Vault (line 10) | @Version(Vault.VERSION)
method modifyHandlerEquals (line 23) | public boolean modifyHandlerEquals(Object o) {
FILE: src/main/java/de/koudingspawn/vault/crd/VaultChangeAdjustmentCallback.java
class VaultChangeAdjustmentCallback (line 5) | public class VaultChangeAdjustmentCallback {
method VaultChangeAdjustmentCallback (line 10) | public VaultChangeAdjustmentCallback() {
method getType (line 13) | public String getType() {
method setType (line 17) | public void setType(String type) {
method getName (line 21) | public String getName() {
method setName (line 25) | public void setName(String name) {
method toString (line 29) | @Override
method equals (line 34) | @Override
method hashCode (line 42) | @Override
FILE: src/main/java/de/koudingspawn/vault/crd/VaultDockerCfgConfiguration.java
class VaultDockerCfgConfiguration (line 5) | public class VaultDockerCfgConfiguration {
method VaultDockerCfgConfiguration (line 10) | public VaultDockerCfgConfiguration() {
method getType (line 15) | public VaultType getType() {
method setType (line 19) | public void setType(VaultType version) {
method getVersion (line 23) | public Integer getVersion() {
method setVersion (line 27) | public void setVersion(Integer version) {
method equals (line 31) | @Override
method hashCode (line 39) | @Override
FILE: src/main/java/de/koudingspawn/vault/crd/VaultJKSConfiguration.java
class VaultJKSConfiguration (line 5) | public class VaultJKSConfiguration {
method getPassword (line 12) | public String getPassword() {
method setPassword (line 16) | public void setPassword(String password) {
method getAlias (line 20) | public String getAlias() {
method setAlias (line 24) | public void setAlias(String alias) {
method getKeyName (line 28) | public String getKeyName() {
method setKeyName (line 32) | public void setKeyName(String keyName) {
method getCaAlias (line 36) | public String getCaAlias() {
method setCaAlias (line 40) | public void setCaAlias(String caAlias) {
method equals (line 44) | @Override
method hashCode (line 52) | @Override
FILE: src/main/java/de/koudingspawn/vault/crd/VaultList.java
class VaultList (line 5) | public class VaultList extends DefaultKubernetesResourceList<Vault> {
FILE: src/main/java/de/koudingspawn/vault/crd/VaultPkiConfiguration.java
class VaultPkiConfiguration (line 5) | public class VaultPkiConfiguration {
method getCommonName (line 12) | public String getCommonName() {
method setCommonName (line 16) | public void setCommonName(String commonName) {
method getAltNames (line 20) | public String getAltNames() {
method setAltNames (line 24) | public void setAltNames(String altNames) {
method getIpSans (line 28) | public String getIpSans() {
method setIpSans (line 32) | public void setIpSans(String ipSans) {
method getTtl (line 36) | public String getTtl() {
method setTtl (line 40) | public void setTtl(String ttl) {
method equals (line 44) | @Override
method hashCode (line 52) | @Override
FILE: src/main/java/de/koudingspawn/vault/crd/VaultPropertiesConfiguration.java
class VaultPropertiesConfiguration (line 6) | public class VaultPropertiesConfiguration {
method getFiles (line 11) | public HashMap<String, String> getFiles() {
method setFiles (line 15) | public void setFiles(HashMap<String, String> files) {
method getContext (line 19) | public HashMap<String, String> getContext() {
method setContext (line 23) | public void setContext(HashMap<String, String> context) {
method equals (line 27) | @Override
method hashCode (line 35) | @Override
FILE: src/main/java/de/koudingspawn/vault/crd/VaultSpec.java
class VaultSpec (line 9) | @JsonDeserialize
method getPath (line 21) | public String getPath() {
method setPath (line 25) | public void setPath(String path) {
method getType (line 29) | public VaultType getType() {
method setType (line 33) | public void setType(VaultType type) {
method getPkiConfiguration (line 37) | public VaultPkiConfiguration getPkiConfiguration() {
method setPkiConfiguration (line 41) | public void setPkiConfiguration(VaultPkiConfiguration pkiConfiguration) {
method getJksConfiguration (line 45) | public VaultJKSConfiguration getJksConfiguration() {
method setJksConfiguration (line 49) | public void setJksConfiguration(VaultJKSConfiguration jksConfiguration) {
method getVersionConfiguration (line 53) | public VaultVersionedConfiguration getVersionConfiguration() {
method setVersionConfiguration (line 57) | public void setVersionConfiguration(VaultVersionedConfiguration versio...
method getPropertiesConfiguration (line 61) | public VaultPropertiesConfiguration getPropertiesConfiguration() {
method setPropertiesConfiguration (line 65) | public void setPropertiesConfiguration(VaultPropertiesConfiguration pr...
method getDockerCfgConfiguration (line 69) | public VaultDockerCfgConfiguration getDockerCfgConfiguration() {
method setDockerCfgConfiguration (line 73) | public void setDockerCfgConfiguration(VaultDockerCfgConfiguration dock...
method getChangeAdjustmentCallback (line 77) | public VaultChangeAdjustmentCallback getChangeAdjustmentCallback() {
method setChangeAdjustmentCallback (line 81) | public void setChangeAdjustmentCallback(VaultChangeAdjustmentCallback ...
method equals (line 85) | @Override
method hashCode (line 93) | @Override
FILE: src/main/java/de/koudingspawn/vault/crd/VaultType.java
type VaultType (line 3) | public enum VaultType {
FILE: src/main/java/de/koudingspawn/vault/crd/VaultVersionedConfiguration.java
class VaultVersionedConfiguration (line 5) | public class VaultVersionedConfiguration {
method getVersion (line 9) | public Integer getVersion() {
method setVersion (line 13) | public void setVersion(Integer version) {
method equals (line 17) | @Override
method hashCode (line 25) | @Override
FILE: src/main/java/de/koudingspawn/vault/kubernetes/ChangeAdjustmentService.java
class ChangeAdjustmentService (line 12) | @Service
method ChangeAdjustmentService (line 19) | public ChangeAdjustmentService(KubernetesClient client) {
method handle (line 23) | public void handle(Vault resource) {
method rotateDeployment (line 40) | private void rotateDeployment(String namespace, String name) {
method rotateStatefulSet (line 61) | private void rotateStatefulSet(String namespace, String name) {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/EventHandler.java
class EventHandler (line 14) | @Component
method EventHandler (line 25) | public EventHandler(VaultService vaultService,
method addHandler (line 37) | public void addHandler(Vault resource) {
method deleteHandler (line 58) | void deleteHandler(Vault resource) {
method modifyHandler (line 64) | public void modifyHandler(Vault resource) {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/KubernetesConnection.java
class KubernetesConnection (line 19) | @Configuration
method testClient (line 24) | @Bean
method client (line 33) | @Bean
method customResource (line 39) | @Bean
FILE: src/main/java/de/koudingspawn/vault/kubernetes/KubernetesService.java
class KubernetesService (line 25) | @Component
method KubernetesService (line 35) | public KubernetesService(KubernetesClient client,
method exists (line 45) | boolean exists(Vault resource) {
method newSecretInstance (line 49) | private Secret newSecretInstance(Vault resource, VaultSecret vaultSecr...
method createSecret (line 58) | void createSecret(Vault resource, VaultSecret vaultSecret) {
method deleteSecret (line 67) | void deleteSecret(ObjectMeta resourceMetadata) {
method modifySecret (line 74) | void modifySecret(Vault resource, VaultSecret vaultSecret) {
method getSecretByVault (line 94) | public Secret getSecretByVault(Vault resource) {
method metaData (line 98) | private ObjectMeta metaData(ObjectMeta resource, String compare) {
method getOwnerReference (line 123) | private List<OwnerReference> getOwnerReference(ObjectMeta resource) {
method hasBrokenOwnerReference (line 140) | public boolean hasBrokenOwnerReference(Vault resource) {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/Watcher.java
class Watcher (line 19) | @Configuration
method Watcher (line 30) | public Watcher(EventHandler eventHandler, KubernetesClient client, Sch...
method watchForResource (line 38) | @Bean
method run (line 43) | private void run() {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/cache/SecretCache.java
class SecretCache (line 13) | public class SecretCache {
method SecretCache (line 20) | public SecretCache(KubernetesClient client, boolean watch) {
method watcher (line 28) | public void watcher() {
method get (line 59) | public Secret get(String namespace, String name) {
method invalidate (line 75) | public void invalidate(String namespace, String name) {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/cache/SecretCacheConfiguration.java
class SecretCacheConfiguration (line 8) | @Configuration
method secretCache (line 11) | @Bean
method testSecretCache (line 17) | @Bean
FILE: src/main/java/de/koudingspawn/vault/kubernetes/event/EventNotification.java
class EventNotification (line 19) | @Service
method EventNotification (line 27) | public EventNotification(@Value("${kubernetes.crd.group}") String crdG...
method storeNewEvent (line 32) | public void storeNewEvent(EventType type, String message, Vault resour...
FILE: src/main/java/de/koudingspawn/vault/kubernetes/event/EventType.java
type EventType (line 3) | public enum EventType {
method EventType (line 15) | EventType(String type, String reason) {
method getEventType (line 20) | public String getEventType() {
method getReason (line 24) | public String getReason() {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/RefreshConfiguration.java
class RefreshConfiguration (line 7) | @Configuration
method slfbForTypeRefresh (line 10) | @Bean("typeRefreshFactory")
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/RequiresRefresh.java
type RequiresRefresh (line 6) | public interface RequiresRefresh {
method refreshIsNeeded (line 8) | boolean refreshIsNeeded(Vault resource) throws SecretNotAccessibleExce...
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/ScheduledRefresh.java
class ScheduledRefresh (line 13) | @Component
method ScheduledRefresh (line 22) | public ScheduledRefresh(
method refreshVaultResource (line 31) | public void refreshVaultResource(Vault resource) {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/TypeRefreshFactory.java
type TypeRefreshFactory (line 3) | public interface TypeRefreshFactory {
method get (line 5) | RequiresRefresh get(String name);
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CertJksRefresh.java
class CertJksRefresh (line 8) | @Component("CERTJKS")
method CertJksRefresh (line 13) | public CertJksRefresh(CertRefresh certRefresh) {
method refreshIsNeeded (line 17) | @Override
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CertRefresh.java
class CertRefresh (line 13) | @Component("CERT")
method CertRefresh (line 20) | public CertRefresh(@Value("${kubernetes.crd.name}") String crdName,
method refreshIsNeeded (line 28) | @Override
method certHashHasChanged (line 33) | private boolean certHashHasChanged(Vault resource) throws SecretNotAcc...
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CompareHash.java
class CompareHash (line 8) | abstract public class CompareHash {
method hashHasChanged (line 10) | boolean hashHasChanged(Secret secretByVault, String vaultSha256, Strin...
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/DockerCfgRefresh.java
class DockerCfgRefresh (line 13) | @Component("DOCKERCFG")
method DockerCfgRefresh (line 21) | public DockerCfgRefresh(@Value("${kubernetes.crd.name}") String crdName,
method refreshIsNeeded (line 29) | public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessi...
method dockerCfgHashHasChanged (line 33) | private boolean dockerCfgHashHasChanged(Vault resource) throws SecretN...
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/KeyValueRefresh.java
class KeyValueRefresh (line 12) | @Component("KEYVALUE")
method KeyValueRefresh (line 19) | public KeyValueRefresh(@Value("${kubernetes.crd.name}") String crdName,
method refreshIsNeeded (line 27) | public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessi...
method certHashHasChanged (line 31) | private boolean certHashHasChanged(Vault resource) throws SecretNotAcc...
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/KeyValueV2Refresh.java
class KeyValueV2Refresh (line 12) | @Component("KEYVALUEV2")
method KeyValueV2Refresh (line 19) | public KeyValueV2Refresh(@Value("${kubernetes.crd.name}") String crdName,
method refreshIsNeeded (line 27) | public boolean refreshIsNeeded(Vault resource) throws SecretNotAccessi...
method hashHasChanged (line 31) | private boolean hashHasChanged(Vault resource) throws SecretNotAccessi...
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PkiJksRefresh.java
class PkiJksRefresh (line 7) | @Component("PKIJKS")
method PkiJksRefresh (line 12) | public PkiJksRefresh(PkiRefresh pkiRefresh) {
method refreshIsNeeded (line 16) | @Override
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PkiRefresh.java
class PkiRefresh (line 21) | @Component("PKI")
method PkiRefresh (line 30) | public PkiRefresh(@Value("${kubernetes.interval}") int interval, @Valu...
method refreshIsNeeded (line 36) | public boolean refreshIsNeeded(Vault resource) {
method certificateIsNearExpirationDate (line 41) | private boolean certificateIsNearExpirationDate(Secret secretByVault) {
method parseDate (line 61) | private Optional<Date> parseDate(String date) {
FILE: src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PropertiesRefresh.java
class PropertiesRefresh (line 8) | @Component("PROPERTIES")
method refreshIsNeeded (line 11) | @Override
FILE: src/main/java/de/koudingspawn/vault/vault/TypedSecretGenerator.java
type TypedSecretGenerator (line 7) | public interface TypedSecretGenerator {
method generateSecret (line 9) | VaultSecret generateSecret(Vault resource) throws SecretNotAccessibleE...
method getHash (line 11) | String getHash(VaultSpec spec) throws SecretNotAccessibleException;
FILE: src/main/java/de/koudingspawn/vault/vault/TypedSecretGeneratorFactory.java
type TypedSecretGeneratorFactory (line 3) | public interface TypedSecretGeneratorFactory {
method get (line 5) | TypedSecretGenerator get(String name);
FILE: src/main/java/de/koudingspawn/vault/vault/VaultCommunication.java
class VaultCommunication (line 31) | @Component
method VaultCommunication (line 39) | public VaultCommunication(VaultTemplate vaultTemplate) {
method createPki (line 43) | public PKIResponse createPki(String path, VaultPkiConfiguration config...
method getCert (line 59) | public PKIResponse getCert(String path) throws SecretNotAccessibleExce...
method getDockerCfg (line 63) | public PullSecret getDockerCfg(String path, VaultDockerCfgConfiguratio...
method getKeyValue (line 71) | public HashMap getKeyValue(String path) throws SecretNotAccessibleExce...
method getRequest (line 75) | private <T> T getRequest(String path, Class<T> clazz) throws SecretNot...
method generateRequest (line 89) | private PKIRequest generateRequest(VaultPkiConfiguration configuration) {
method getVersionedSecret (line 110) | public HashMap getVersionedSecret(String path, Optional<Integer> versi...
method getVersionedSecret (line 114) | private <T> T getVersionedSecret(String path, Optional<Integer> versio...
method isHealthy (line 140) | public boolean isHealthy() {
method doWithRestOperations (line 144) | private boolean doWithRestOperations(RestOperations restOperations) {
method extractMountPoint (line 154) | private String extractMountPoint(String path) throws SecretNotAccessib...
method extractKey (line 162) | private String extractKey(String path) throws SecretNotAccessibleExcep...
FILE: src/main/java/de/koudingspawn/vault/vault/VaultConfiguration.java
class VaultConfiguration (line 17) | @Configuration
method slfbForTypeRefresh (line 20) | @Bean
class VaultTokenConnection (line 27) | @Configuration
method VaultTokenConnection (line 34) | VaultTokenConnection(@Value("${kubernetes.vault.token}") String vaul...
method vaultEndpoint (line 40) | @Override
method clientAuthentication (line 45) | @Override
class VaultServiceAccountConnection (line 52) | @Configuration
method VaultServiceAccountConnection (line 60) | VaultServiceAccountConnection(@Value("${kubernetes.vault.url}") Stri...
method vaultEndpoint (line 68) | @Override
method clientAuthentication (line 73) | @Override
method getVaultUrlWithoutPath (line 82) | private URI getVaultUrlWithoutPath(String vaultUrl) {
FILE: src/main/java/de/koudingspawn/vault/vault/VaultHealthCheck.java
class VaultHealthCheck (line 7) | @Component
method VaultHealthCheck (line 12) | public VaultHealthCheck(VaultCommunication vaultCommunication) {
method health (line 16) | @Override
FILE: src/main/java/de/koudingspawn/vault/vault/VaultSecret.java
class VaultSecret (line 5) | public class VaultSecret {
method VaultSecret (line 13) | public VaultSecret(Map<String, String> data, String compare) {
method VaultSecret (line 19) | public VaultSecret(Map<String, String> data, String compare, String ty...
method getData (line 25) | public Map<String, String> getData() {
method setData (line 29) | public void setData(Map<String, String> data) {
method getCompare (line 33) | public String getCompare() {
method setCompare (line 37) | public void setCompare(String compare) {
method getType (line 41) | public String getType() {
method setType (line 45) | public void setType(String type) {
FILE: src/main/java/de/koudingspawn/vault/vault/VaultService.java
class VaultService (line 7) | @Component
method VaultService (line 12) | public VaultService(TypedSecretGeneratorFactory typedSecretGeneratorFa...
method generateSecret (line 16) | public VaultSecret generateSecret(Vault resource) throws SecretNotAcce...
FILE: src/main/java/de/koudingspawn/vault/vault/communication/SecretNotAccessibleException.java
class SecretNotAccessibleException (line 3) | public class SecretNotAccessibleException extends Exception {
method SecretNotAccessibleException (line 5) | public SecretNotAccessibleException(String message) {
method SecretNotAccessibleException (line 9) | public SecretNotAccessibleException(String message, Throwable cause) {
FILE: src/main/java/de/koudingspawn/vault/vault/communication/TokenLookup.java
class TokenLookup (line 3) | public class TokenLookup {
method getRequest_id (line 9) | public String getRequest_id() {
method setRequest_id (line 13) | public void setRequest_id(String request_id) {
method isRenewable (line 17) | public boolean isRenewable() {
method setRenewable (line 21) | public void setRenewable(boolean renewable) {
method getData (line 25) | public TokenLookupData getData() {
method setData (line 29) | public void setData(TokenLookupData data) {
FILE: src/main/java/de/koudingspawn/vault/vault/communication/TokenLookupData.java
class TokenLookupData (line 3) | public class TokenLookupData {
method getAccessor (line 9) | public String getAccessor() {
method setAccessor (line 13) | public void setAccessor(String accessor) {
method getDisplay_name (line 17) | public String getDisplay_name() {
method setDisplay_name (line 21) | public void setDisplay_name(String display_name) {
method getId (line 25) | public String getId() {
method setId (line 29) | public void setId(String id) {
method getPath (line 33) | public String getPath() {
method setPath (line 37) | public void setPath(String path) {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/CertGenerator.java
class CertGenerator (line 13) | @Component("CERTGENERATOR")
method CertGenerator (line 19) | public CertGenerator(VaultCommunication vaultCommunication, SharedVaul...
method generateSecret (line 24) | @Override
method getHash (line 31) | @Override
FILE: src/main/java/de/koudingspawn/vault/vault/impl/CertJksGenerator.java
class CertJksGenerator (line 12) | @Component("CERTJKSGENERATOR")
method CertJksGenerator (line 18) | public CertJksGenerator(VaultCommunication vaultCommunication, SharedV...
method generateSecret (line 23) | @Override
method getHash (line 29) | @Override
FILE: src/main/java/de/koudingspawn/vault/vault/impl/DockerCfgGenerator.java
class DockerCfgGenerator (line 18) | @Component("DOCKERCFGGENERATOR")
method DockerCfgGenerator (line 23) | public DockerCfgGenerator(VaultCommunication vaultCommunication) {
method generateSecret (line 27) | @Override
method getHash (line 33) | @Override
method getConfiguration (line 43) | private VaultDockerCfgConfiguration getConfiguration(VaultSpec spec) {
method mapDockerCfg (line 47) | private VaultSecret mapDockerCfg(PullSecret pullSecret) {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/EncryptionUtils.java
class EncryptionUtils (line 12) | public class EncryptionUtils {
method EncryptionUtils (line 18) | private EncryptionUtils() {
method loadPrivateKey (line 24) | public static PrivateKey loadPrivateKey(String keyDataString) throws G...
method readPkcs8PrivateKey (line 50) | private static PrivateKey readPkcs8PrivateKey(byte[] pkcs8Bytes) throw...
method readPkcs1PrivateKey (line 63) | private static PrivateKey readPkcs1PrivateKey(byte[] pkcs1Bytes) throw...
method join (line 77) | private static byte[] join(byte[] byteArray1, byte[] byteArray2) {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/KeyValueGenerator.java
class KeyValueGenerator (line 17) | @Component("KEYVALUEGENERATOR")
method KeyValueGenerator (line 22) | public KeyValueGenerator(VaultCommunication vaultCommunication) {
method generateSecret (line 26) | @Override
method getHash (line 32) | @Override
method mapKeyValueResponse (line 42) | private VaultSecret mapKeyValueResponse(HashMap<String, String> keyVal...
FILE: src/main/java/de/koudingspawn/vault/vault/impl/KeyValueV2Generator.java
class KeyValueV2Generator (line 14) | @Component("KEYVALUEV2GENERATOR")
method KeyValueV2Generator (line 19) | public KeyValueV2Generator(VaultCommunication vaultCommunication) {
method generateSecret (line 23) | @Override
method getHash (line 31) | @Override
method mapKeyValueResponse (line 42) | private VaultSecret mapKeyValueResponse(HashMap<String, String> keyVal...
method getVersion (line 56) | private Optional<Integer> getVersion(VaultSpec resource) {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/PkiJksGenerator.java
class PkiJksGenerator (line 12) | @Component("PKIJKSGENERATOR")
method PkiJksGenerator (line 18) | public PkiJksGenerator(VaultCommunication vaultCommunication, SharedVa...
method generateSecret (line 23) | @Override
method getHash (line 29) | @Override
FILE: src/main/java/de/koudingspawn/vault/vault/impl/PkiSecretGenerator.java
class PkiSecretGenerator (line 12) | @Component("PKIGENERATOR")
method PkiSecretGenerator (line 18) | public PkiSecretGenerator(VaultCommunication vaultCommunication, Share...
method generateSecret (line 23) | @Override
method getHash (line 30) | @Override
FILE: src/main/java/de/koudingspawn/vault/vault/impl/PropertiesGenerator.java
class PropertiesGenerator (line 20) | @Component("PROPERTIESGENERATOR")
method PropertiesGenerator (line 25) | public PropertiesGenerator(VaultCommunication vaultCommunication) {
method generateSecret (line 29) | @Override
method getHash (line 52) | @Override
method renderFiles (line 57) | private Map<String, String> renderFiles(Map<String, Object> context, M...
FILE: src/main/java/de/koudingspawn/vault/vault/impl/Sha256.java
class Sha256 (line 8) | public class Sha256 {
method generateSha256 (line 10) | public static String generateSha256(String ... args) {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/SharedVaultResponseMapper.java
class SharedVaultResponseMapper (line 28) | @Component
method mapPki (line 38) | VaultSecret mapPki(VaultResponseData responseData) throws SecretNotAcc...
method mapCert (line 54) | VaultSecret mapCert(VaultResponseData vaultResponseData) {
method mappedCertValues (line 64) | private Map<String, String> mappedCertValues(VaultResponseData vaultRe...
method getCrt (line 75) | private String getCrt(VaultResponseData responseData) {
method getKey (line 79) | private String getKey(VaultResponseData responseData) {
method mapJks (line 83) | VaultSecret mapJks(VaultResponseData data, VaultJKSConfiguration jksCo...
method getAlias (line 133) | private String getAlias(VaultJKSConfiguration jksConfiguration) {
method getPassword (line 140) | private String getPassword(VaultJKSConfiguration jksConfiguration) {
method getKey (line 147) | private String getKey(VaultJKSConfiguration jksConfiguration) {
method getPublicKey (line 154) | private Certificate[] getPublicKey(String pem) throws CertificateExcep...
method getCertificateWithShortestLivetime (line 159) | private X509Certificate getCertificateWithShortestLivetime(Certificate...
FILE: src/main/java/de/koudingspawn/vault/vault/impl/dockercfg/PullSecret.java
class PullSecret (line 5) | public class PullSecret {
method getUsername (line 12) | public String getUsername() {
method setUsername (line 16) | public void setUsername(String username) {
method getPassword (line 20) | public String getPassword() {
method setPassword (line 24) | public void setPassword(String password) {
method getEmail (line 28) | public String getEmail() {
method setEmail (line 32) | public void setEmail(String email) {
method getUrl (line 36) | public String getUrl() {
method setUrl (line 40) | public void setUrl(String url) {
method getAuth (line 44) | public String getAuth() {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/pki/PKIRequest.java
class PKIRequest (line 3) | public class PKIRequest {
method getCommon_name (line 10) | public String getCommon_name() {
method setCommon_name (line 14) | public void setCommon_name(String common_name) {
method getAlt_names (line 18) | public String getAlt_names() {
method setAlt_names (line 22) | public void setAlt_names(String alt_names) {
method getIp_sans (line 26) | public String getIp_sans() {
method setIp_sans (line 30) | public void setIp_sans(String ip_sans) {
method getFormat (line 34) | public String getFormat() {
method setFormat (line 38) | public void setFormat(String format) {
method getTtl (line 42) | public String getTtl() {
method setTtl (line 46) | public void setTtl(String ttl) {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/pki/PKIResponse.java
class PKIResponse (line 3) | public class PKIResponse {
method getLease_id (line 8) | public String getLease_id() {
method setLease_id (line 12) | public void setLease_id(String lease_id) {
method isRenewable (line 16) | public boolean isRenewable() {
method setRenewable (line 20) | public void setRenewable(boolean renewable) {
method getData (line 24) | public VaultResponseData getData() {
method setData (line 28) | public void setData(VaultResponseData data) {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/pki/VaultResponseData.java
class VaultResponseData (line 7) | public class VaultResponseData {
method getCertificate (line 15) | public String getCertificate() {
method setCertificate (line 19) | public void setCertificate(String certificate) {
method getIssuing_ca (line 23) | public String getIssuing_ca() {
method setIssuing_ca (line 27) | public void setIssuing_ca(String issuing_ca) {
method getCa_chain (line 31) | public List<String> getCa_chain() {
method setCa_chain (line 35) | public void setCa_chain(List<String> ca_chain) {
method getPrivate_key (line 39) | public String getPrivate_key() {
method setPrivate_key (line 43) | public void setPrivate_key(String private_key) {
method getPrivate_key_type (line 47) | public String getPrivate_key_type() {
method setPrivate_key_type (line 51) | public void setPrivate_key_type(String private_key_type) {
method getSerial_number (line 55) | public String getSerial_number() {
method setSerial_number (line 59) | public void setSerial_number(String serial_number) {
method getChainedCertificate (line 63) | public String getChainedCertificate() {
FILE: src/main/java/de/koudingspawn/vault/vault/impl/properties/VaultJinjaLookup.java
class VaultJinjaLookup (line 9) | public class VaultJinjaLookup {
method VaultJinjaLookup (line 13) | public VaultJinjaLookup(VaultCommunication vaultCommunication) {
method lookup (line 17) | public String lookup(String path, String key) throws SecretNotAccessib...
method lookup (line 21) | public HashMap lookup(String path) throws SecretNotAccessibleException {
method lookupV2 (line 25) | public HashMap lookupV2(String path) throws SecretNotAccessibleExcepti...
method lookupV2 (line 29) | public String lookupV2(String path, String key) throws SecretNotAccess...
method lookupV2 (line 38) | public String lookupV2(String path, int version, String key) throws Se...
FILE: src/test/java/de/koudingspawn/vault/CertChainTest.java
class CertChainTest (line 35) | @RunWith(SpringRunner.class)
class KindConfig (line 57) | @org.springframework.boot.test.context.TestConfiguration
method client (line 60) | @Bean
method before (line 71) | @Before
method shouldGenerateCertFromVaultResource (line 79) | @Test
method shouldCheckIfCertificateHasChangedAndReturnFalse (line 132) | @Test
method shouldCheckIfCertificateHasChangedAndReturnTrue (line 171) | @Test
FILE: src/test/java/de/koudingspawn/vault/CertTest.java
class CertTest (line 35) | @RunWith(SpringRunner.class)
class KindConfig (line 57) | @org.springframework.boot.test.context.TestConfiguration
method client (line 60) | @Bean
method before (line 71) | @Before
method shouldGenerateCertFromVaultResource (line 79) | @Test
method shouldCheckIfCertificateHasChangedAndReturnFalse (line 131) | @Test
method shouldCheckIfCertificateHasChangedAndReturnTrue (line 169) | @Test
FILE: src/test/java/de/koudingspawn/vault/DockerCfgTest.java
class DockerCfgTest (line 40) | @RunWith(SpringRunner.class)
class KindConfig (line 65) | @org.springframework.boot.test.context.TestConfiguration
method client (line 68) | @Bean
method before (line 76) | @Before
method shouldGenerateDockerCfgFromVaultResource (line 84) | @Test
method shouldCheckIfDockerCfgHasChangedAndReturnTrue (line 141) | @Test
method shouldCheckIfDockerCfgHasChangedAndReturnFalse (line 173) | @Test
method shouldGenerateDockerCfgV2 (line 196) | @Test
method cleanup (line 262) | @After
FILE: src/test/java/de/koudingspawn/vault/EventNotificationTest.java
class EventNotificationTest (line 24) | @RunWith(SpringRunner.class)
class KindConfig (line 38) | @org.springframework.boot.test.context.TestConfiguration
method client (line 41) | @Bean
method shouldBeAbleToCreateEvent (line 48) | @Test
FILE: src/test/java/de/koudingspawn/vault/KeyValueTest.java
class KeyValueTest (line 36) | @RunWith(SpringRunner.class)
class KindConfig (line 60) | @org.springframework.boot.test.context.TestConfiguration
method client (line 63) | @Bean
method before (line 71) | @Before
method shouldGenerateSimpleSecretFromVaultCustomResource (line 79) | @Test
method shouldCheckIfSimpleSecretHasChangedAndReturnTrue (line 106) | @Test
method shouldCheckIfSimpleSecretHasChangedAndReturnFalse (line 138) | @Test
method preventNullPointerExceptionWhenSecretDoesNotExist (line 159) | @Test
FILE: src/test/java/de/koudingspawn/vault/KeyValueV2Test.java
class KeyValueV2Test (line 36) | @RunWith(SpringRunner.class)
class KindConfig (line 61) | @org.springframework.boot.test.context.TestConfiguration
method client (line 64) | @Bean
method before (line 72) | @Before
method shouldGenerateSimpleSecretFromVaultCustomResource (line 80) | @Test
method shouldCheckIfSimpleSecretHasChangedAndReturnTrue (line 127) | @Test
method shouldCheckIfSimpleSecretHasChangedAndReturnFalse (line 199) | @Test
method shouldSupportNestedPath (line 240) | @Test
FILE: src/test/java/de/koudingspawn/vault/OwnerReferenceBugfixTest.java
class OwnerReferenceBugfixTest (line 36) | @RunWith(SpringRunner.class)
class KindConfig (line 62) | @org.springframework.boot.test.context.TestConfiguration
method client (line 65) | @Bean
method before (line 73) | @Before
method hasCorrectOwnerReference (line 81) | @Test
method fixOwnerReference (line 94) | @Test
FILE: src/test/java/de/koudingspawn/vault/PKIChainTest.java
class PKIChainTest (line 39) | @RunWith(SpringRunner.class)
class KindConfig (line 60) | @org.springframework.boot.test.context.TestConfiguration
method client (line 63) | @Bean
method before (line 74) | @Before
method shouldGeneratePkiFromVaultChainResource (line 82) | @Test
method cleanup (line 140) | @After
method generateKeyPair (line 149) | private VaultResponseData generateKeyPair() {
method generateVaultResource (line 248) | private Vault generateVaultResource() {
method convertDate (line 268) | private LocalDateTime convertDate(Date date) {
method parseDate (line 274) | private LocalDateTime parseDate(String date) throws ParseException {
FILE: src/test/java/de/koudingspawn/vault/PKITest.java
class PKITest (line 54) | @RunWith(SpringRunner.class)
class KindConfig (line 79) | @org.springframework.boot.test.context.TestConfiguration
method client (line 82) | @Bean
method before (line 93) | @Before
method shouldGeneratePkiFromVaultResource (line 101) | @Test
method cleanup (line 158) | @After
method generateKeyPair (line 167) | private VaultResponseData generateKeyPair(Date startDate, long valid) ...
method generateVaultResource (line 208) | private Vault generateVaultResource() {
method convertDate (line 228) | private LocalDateTime convertDate(Date date) {
method parseDate (line 234) | private LocalDateTime parseDate(String date) throws ParseException {
FILE: src/test/java/de/koudingspawn/vault/PropertiesTest.java
class PropertiesTest (line 40) | @RunWith(SpringRunner.class)
class KindConfig (line 66) | @org.springframework.boot.test.context.TestConfiguration
method client (line 69) | @Bean
method before (line 80) | @Before
method shouldRenderPropertiesFile (line 88) | @Test
method shouldFailRenderSecret (line 110) | @Test(expected = SecretNotAccessibleException.class)
method cleanup (line 119) | @After
method generatePropertiesManifest (line 128) | static Vault generatePropertiesManifest(String name) throws IOException {
FILE: src/test/java/de/koudingspawn/vault/TestHelper.java
class TestHelper (line 9) | public class TestHelper {
method generateLookupSelfStub (line 11) | public static void generateLookupSelfStub() {
method generateKVStup (line 46) | public static void generateKVStup(String path, Map<String, String> val...
method generateKV2Stup (line 58) | public static void generateKV2Stup(String path, Map<String, String> va...
FILE: src/test/java/de/koudingspawn/vault/admissionreview/AdmissionReviewTest.java
class AdmissionReviewTest (line 29) | @RunWith(SpringRunner.class)
method shouldFailWithInvalidRequest (line 49) | @Test
method shouldReturnValidValue (line 83) | @Test
FILE: src/test/java/de/koudingspawn/vault/kubernetes/EventHandlerTest.java
class EventHandlerTest (line 19) | @RunWith(SpringRunner.class)
method setup (line 36) | @Before
method shouldGenerateKubernetesSecret (line 42) | @Test
method shouldDoNotingIfSecretForVaultAlreadyExists (line 53) | @Test
method shouldDoNothingIfGenerateSecretFails (line 63) | @Test
method shouldModifySecret (line 73) | @Test
method shouldDoNothingIfCreateSecretForModificationFails (line 85) | @Test
FILE: src/test/java/de/koudingspawn/vault/kubernetes/KubernetesServiceTest.java
class KubernetesServiceTest (line 25) | @RunWith(SpringRunner.class)
class KindConfig (line 40) | @org.springframework.boot.test.context.TestConfiguration
method client (line 43) | @Bean
method setUp (line 50) | @Before
method shouldCheckIfResourceExists (line 59) | @Test
method shouldFindNoResource (line 71) | @Test
method shouldCreateSecret (line 80) | @Test
method shouldDeleteSecret (line 94) | @Test
method shouldModifySecret (line 107) | @Test
method cleanup (line 127) | @After
method generateSecret (line 136) | private Secret generateSecret() {
method generateVaultSecret (line 148) | private VaultSecret generateVaultSecret() {
method generateVault (line 154) | private Vault generateVault() {
FILE: src/test/java/de/koudingspawn/vault/vault/VaultHealthCheckTest.java
class VaultHealthCheckTest (line 13) | @RunWith(SpringRunner.class)
method shouldReturnUnhealthyIfVaultCommunicationFails (line 22) | @Test
method shouldReturnHealthyResultIfVaultCommunicationWorks (line 29) | @Test
Condensed preview — 110 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (302K chars).
[
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 1973,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": ".github/workflows/maven.yaml",
"chars": 2229,
"preview": "name: Java CI\n\non: [push]\n\njobs:\n test:\n name: \"Test\"\n\n runs-on: ubuntu-latest\n\n strategy:\n matrix:\n "
},
{
"path": ".gitignore",
"chars": 30,
"preview": "target/*\n.idea/*\n*.iml\nvault/*"
},
{
"path": ".mvn/wrapper/maven-wrapper.properties",
"chars": 110,
"preview": "distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip\n"
},
{
"path": "Dockerfile",
"chars": 843,
"preview": "FROM gcr.io/distroless/java17-debian11:nonroot AS SECURITY\nFROM openjdk:17 AS BUILD\n\nCOPY . /opt\nWORKDIR /opt\nRUN ./mvnw"
},
{
"path": "LICENSE",
"chars": 11361,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "crd.yml",
"chars": 2757,
"preview": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n name: vault.koudingspawn.de\nspec:\n group"
},
{
"path": "deploy/admission-webhook.yaml",
"chars": 2377,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: vault-crd\n namespace: vault-crd\nspec:\n selector:\n app: vault-crd\n p"
},
{
"path": "deploy/rbac.yaml",
"chars": 10122,
"preview": "apiVersion: v1\nkind: Namespace\nmetadata:\n name: vault-crd\n\n---\n\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: v"
},
{
"path": "examples/cert.yml",
"chars": 137,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-cert\nspec:\n path: \"keyvaluev1/vault.koudingspawn.de"
},
{
"path": "examples/certjks-rollout-redo.yml",
"chars": 603,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: nginx\n labels:\n app: nginx\nspec:\n replicas: 1\n template:\n "
},
{
"path": "examples/certjks.yml",
"chars": 143,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-certjks\nspec:\n path: \"keyvaluev1/vault.koudingspawn"
},
{
"path": "examples/dockercfg-error.yml",
"chars": 136,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-dockercfg-error\nspec:\n path: \"blub/docker-hub\"\n ty"
},
{
"path": "examples/dockercfg.yml",
"chars": 136,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-dockercfg\nspec:\n path: \"keyvaluev1/docker-hub\"\n ty"
},
{
"path": "examples/keyvalue.yml",
"chars": 134,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-keyvalue\nspec:\n path: \"keyvaluev1/docker-hub\"\n typ"
},
{
"path": "examples/keyvaluev2-version.yml",
"chars": 173,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-keyvaluev2\nspec:\n path: \"keyvaluev2/example\"\n type"
},
{
"path": "examples/keyvaluev2.yml",
"chars": 134,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-keyvaluev2\nspec:\n path: \"keyvaluev2/example\"\n type"
},
{
"path": "examples/kind/cluster.yaml",
"chars": 154,
"preview": "kind: Cluster\napiVersion: kind.x-k8s.io/v1alpha4\nnodes:\n - role: control-plane\n extraPortMappings:\n - container"
},
{
"path": "examples/kind/run.sh",
"chars": 1941,
"preview": "#!/usr/bin/env bash\n\n### setup kind cluster\nkind create cluster --config $PWD/cluster.yaml\n### it exposes at 8200 a port"
},
{
"path": "examples/kind/vault.yaml",
"chars": 6313,
"preview": "---\n# Source: vault/templates/server-serviceaccount.yaml\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: vault\n n"
},
{
"path": "examples/pki.yml",
"chars": 199,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-pki\nspec:\n path: \"testpki/issue/testrole\"\n type: \""
},
{
"path": "examples/pki_chain.yml",
"chars": 187,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-pki\nspec:\n path: \"pki_int/issue/testrole\"\n type: \""
},
{
"path": "examples/pkijks.yml",
"chars": 245,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: test-pkijks\nspec:\n path: \"testpki/issue/testrole\"\n type"
},
{
"path": "examples/properties.yml",
"chars": 504,
"preview": "apiVersion: \"koudingspawn.de/v1\"\nkind: Vault\nmetadata:\n name: properties-example\nspec:\n type: \"PROPERTIES\"\n propertie"
},
{
"path": "mvnw",
"chars": 6468,
"preview": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Softwa"
},
{
"path": "mvnw.cmd",
"chars": 4994,
"preview": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software F"
},
{
"path": "pom.xml",
"chars": 3327,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
},
{
"path": "readme.md",
"chars": 722,
"preview": "# What is Vault-CRD?\n\nVault-CRD is a custom resource definition for holding secrets that are stored in HashiCorp Vault u"
},
{
"path": "src/main/java/de/koudingspawn/vault/Constants.java",
"chars": 299,
"preview": "package de.koudingspawn.vault;\n\npublic class Constants {\n\n private Constants() {\n }\n\n public static final Strin"
},
{
"path": "src/main/java/de/koudingspawn/vault/VaultApplication.java",
"chars": 408,
"preview": "package de.koudingspawn.vault;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoc"
},
{
"path": "src/main/java/de/koudingspawn/vault/admissionreview/AdmissionReviewRestService.java",
"chars": 1173,
"preview": "package de.koudingspawn.vault.admissionreview;\n\nimport io.fabric8.kubernetes.api.model.admission.v1.AdmissionResponse;\ni"
},
{
"path": "src/main/java/de/koudingspawn/vault/admissionreview/AdmissionReviewService.java",
"chars": 2392,
"preview": "package de.koudingspawn.vault.admissionreview;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.vau"
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/Vault.java",
"chars": 2916,
"preview": "package de.koudingspawn.vault.crd;\n\nimport io.fabric8.kubernetes.api.model.Namespaced;\nimport io.fabric8.kubernetes.clie"
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultChangeAdjustmentCallback.java",
"chars": 974,
"preview": "package de.koudingspawn.vault.crd;\n\nimport java.util.Objects;\n\npublic class VaultChangeAdjustmentCallback {\n\n private"
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultDockerCfgConfiguration.java",
"chars": 955,
"preview": "package de.koudingspawn.vault.crd;\n\nimport java.util.Objects;\n\npublic class VaultDockerCfgConfiguration {\n\n private V"
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultJKSConfiguration.java",
"chars": 1314,
"preview": "package de.koudingspawn.vault.crd;\n\nimport java.util.Objects;\n\npublic class VaultJKSConfiguration {\n\n private String "
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultList.java",
"chars": 179,
"preview": "package de.koudingspawn.vault.crd;\n\nimport io.fabric8.kubernetes.api.model.DefaultKubernetesResourceList;\n\npublic class "
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultPkiConfiguration.java",
"chars": 1314,
"preview": "package de.koudingspawn.vault.crd;\n\nimport java.util.Objects;\n\npublic class VaultPkiConfiguration {\n\n private String "
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultPropertiesConfiguration.java",
"chars": 1001,
"preview": "package de.koudingspawn.vault.crd;\n\nimport java.util.HashMap;\nimport java.util.Objects;\n\npublic class VaultPropertiesCon"
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultSpec.java",
"chars": 3581,
"preview": "package de.koudingspawn.vault.crd;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.da"
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultType.java",
"chars": 138,
"preview": "package de.koudingspawn.vault.crd;\n\npublic enum VaultType {\n PKI, CERT, DOCKERCFG, KEYVALUE, PKIJKS, CERTJKS, KEYVALU"
},
{
"path": "src/main/java/de/koudingspawn/vault/crd/VaultVersionedConfiguration.java",
"chars": 667,
"preview": "package de.koudingspawn.vault.crd;\n\nimport java.util.Objects;\n\npublic class VaultVersionedConfiguration {\n\n private I"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/ChangeAdjustmentService.java",
"chars": 3823,
"preview": "package de.koudingspawn.vault.kubernetes;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/EventHandler.java",
"chars": 3978,
"preview": "package de.koudingspawn.vault.kubernetes;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.kubernet"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/KubernetesConnection.java",
"chars": 2062,
"preview": "package de.koudingspawn.vault.kubernetes;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/KubernetesService.java",
"chars": 6254,
"preview": "package de.koudingspawn.vault.kubernetes;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.kubernet"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/Watcher.java",
"chars": 3609,
"preview": "package de.koudingspawn.vault.kubernetes;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.kubernet"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/cache/SecretCache.java",
"chars": 3074,
"preview": "package de.koudingspawn.vault.kubernetes.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benm"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/cache/SecretCacheConfiguration.java",
"chars": 628,
"preview": "package de.koudingspawn.vault.kubernetes.cache;\n\nimport io.fabric8.kubernetes.client.KubernetesClient;\nimport org.spring"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/event/EventNotification.java",
"chars": 2741,
"preview": "package de.koudingspawn.vault.kubernetes.event;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport io.fabric8.kubernetes.ap"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/event/EventType.java",
"chars": 744,
"preview": "package de.koudingspawn.vault.kubernetes.event;\n\npublic enum EventType {\n CREATION_SUCCESSFUL(\"Normal\", \"SuccessfulCr"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/RefreshConfiguration.java",
"chars": 557,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler;\n\nimport org.springframework.beans.factory.config.ServiceLocatorFacto"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/RequiresRefresh.java",
"chars": 293,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/ScheduledRefresh.java",
"chars": 1929,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/TypeRefreshFactory.java",
"chars": 133,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler;\n\npublic interface TypeRefreshFactory {\n\n RequiresRefresh get(Stri"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CertJksRefresh.java",
"chars": 683,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CertRefresh.java",
"chars": 1793,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/CompareHash.java",
"chars": 954,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport io.fabric8.kubernetes.api.model.Secret;\nimport org.spri"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/DockerCfgRefresh.java",
"chars": 1811,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/KeyValueRefresh.java",
"chars": 1685,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/KeyValueV2Refresh.java",
"chars": 1689,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PkiJksRefresh.java",
"chars": 558,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PkiRefresh.java",
"chars": 2757,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/kubernetes/scheduler/impl/PropertiesRefresh.java",
"chars": 554,
"preview": "package de.koudingspawn.vault.kubernetes.scheduler.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/TypedSecretGenerator.java",
"chars": 403,
"preview": "package de.koudingspawn.vault.vault;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.VaultSpec"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/TypedSecretGeneratorFactory.java",
"chars": 132,
"preview": "package de.koudingspawn.vault.vault;\n\npublic interface TypedSecretGeneratorFactory {\n\n TypedSecretGenerator get(Strin"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/VaultCommunication.java",
"chars": 7279,
"preview": "package de.koudingspawn.vault.vault;\n\nimport de.koudingspawn.vault.crd.VaultDockerCfgConfiguration;\nimport de.koudingspa"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/VaultConfiguration.java",
"chars": 3186,
"preview": "package de.koudingspawn.vault.vault;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframew"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/VaultHealthCheck.java",
"chars": 704,
"preview": "package de.koudingspawn.vault.vault;\n\nimport org.springframework.boot.actuate.health.Health;\nimport org.springframework."
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/VaultSecret.java",
"chars": 951,
"preview": "package de.koudingspawn.vault.vault;\n\nimport java.util.Map;\n\npublic class VaultSecret {\n\n private Map<String, String>"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/VaultService.java",
"chars": 788,
"preview": "package de.koudingspawn.vault.vault;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.vault.communi"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/communication/SecretNotAccessibleException.java",
"chars": 318,
"preview": "package de.koudingspawn.vault.vault.communication;\n\npublic class SecretNotAccessibleException extends Exception {\n\n p"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/communication/TokenLookup.java",
"chars": 653,
"preview": "package de.koudingspawn.vault.vault.communication;\n\npublic class TokenLookup {\n\n private String request_id;\n priva"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/communication/TokenLookupData.java",
"chars": 775,
"preview": "package de.koudingspawn.vault.vault.communication;\n\npublic class TokenLookupData {\n private String accessor;\n priv"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/CertGenerator.java",
"chars": 1740,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/CertJksGenerator.java",
"chars": 1377,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/DockerCfgGenerator.java",
"chars": 2376,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/EncryptionUtils.java",
"chars": 3922,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport java.security.GeneralSecurityException;\nimport java.security.KeyFactor"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/KeyValueGenerator.java",
"chars": 2056,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/KeyValueV2Generator.java",
"chars": 2424,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/PkiJksGenerator.java",
"chars": 1416,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/PkiSecretGenerator.java",
"chars": 1342,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/PropertiesGenerator.java",
"chars": 2788,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport com.google.common.collect.Maps;\nimport com.hubspot.jinjava.Jinjava;\nim"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/Sha256.java",
"chars": 787,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/SharedVaultResponseMapper.java",
"chars": 7159,
"preview": "package de.koudingspawn.vault.vault.impl;\n\nimport de.koudingspawn.vault.Constants;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/dockercfg/PullSecret.java",
"chars": 964,
"preview": "package de.koudingspawn.vault.vault.impl.dockercfg;\n\nimport java.util.Base64;\n\npublic class PullSecret {\n\n private St"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/pki/PKIRequest.java",
"chars": 970,
"preview": "package de.koudingspawn.vault.vault.impl.pki;\n\npublic class PKIRequest {\n private String common_name;\n private Str"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/pki/PKIResponse.java",
"chars": 639,
"preview": "package de.koudingspawn.vault.vault.impl.pki;\n\npublic class PKIResponse {\n private String lease_id;\n private boole"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/pki/VaultResponseData.java",
"chars": 1689,
"preview": "package de.koudingspawn.vault.vault.impl.pki;\n\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.List;\n"
},
{
"path": "src/main/java/de/koudingspawn/vault/vault/impl/properties/VaultJinjaLookup.java",
"chars": 1835,
"preview": "package de.koudingspawn.vault.vault.impl.properties;\n\nimport de.koudingspawn.vault.vault.VaultCommunication;\nimport de.k"
},
{
"path": "src/main/resources/application.properties",
"chars": 486,
"preview": "kubernetes.crd.group=koudingspawn.de\nkubernetes.crd.name=vault.koudingspawn.de\nkubernetes.vault.auth=token\nkubernetes.va"
},
{
"path": "src/test/java/de/koudingspawn/vault/CertChainTest.java",
"chars": 10063,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/CertTest.java",
"chars": 9709,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/DockerCfgTest.java",
"chars": 12643,
"preview": "package de.koudingspawn.vault;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson."
},
{
"path": "src/test/java/de/koudingspawn/vault/EventNotificationTest.java",
"chars": 2251,
"preview": "package de.koudingspawn.vault;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.kubernetes.event.Ev"
},
{
"path": "src/test/java/de/koudingspawn/vault/KeyValueTest.java",
"chars": 8555,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/KeyValueV2Test.java",
"chars": 12888,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/OwnerReferenceBugfixTest.java",
"chars": 5053,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/PKIChainTest.java",
"chars": 14955,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/PKITest.java",
"chars": 9838,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/PropertiesTest.java",
"chars": 6026,
"preview": "package de.koudingspawn.vault;\n\nimport com.github.tomakehurst.wiremock.client.WireMock;\nimport com.github.tomakehurst.wi"
},
{
"path": "src/test/java/de/koudingspawn/vault/TestHelper.java",
"chars": 4174,
"preview": "package de.koudingspawn.vault;\n\nimport org.json.JSONObject;\n\nimport java.util.Map;\n\nimport static com.github.tomakehurst"
},
{
"path": "src/test/java/de/koudingspawn/vault/admissionreview/AdmissionReviewTest.java",
"chars": 4999,
"preview": "package de.koudingspawn.vault.admissionreview;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd"
},
{
"path": "src/test/java/de/koudingspawn/vault/kubernetes/EventHandlerTest.java",
"chars": 3029,
"preview": "package de.koudingspawn.vault.kubernetes;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.crd.Vaul"
},
{
"path": "src/test/java/de/koudingspawn/vault/kubernetes/KubernetesServiceTest.java",
"chars": 5496,
"preview": "package de.koudingspawn.vault.kubernetes;\n\nimport de.koudingspawn.vault.crd.Vault;\nimport de.koudingspawn.vault.kubernet"
},
{
"path": "src/test/java/de/koudingspawn/vault/vault/VaultHealthCheckTest.java",
"chars": 981,
"preview": "package de.koudingspawn.vault.vault;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.InjectM"
},
{
"path": "src/test/resources/application.properties",
"chars": 540,
"preview": "kubernetes.crd.group=something-not-garbage-collected.de\nkubernetes.crd.name=vault.koudingspawn.de\nkubernetes.vault.auth="
},
{
"path": "src/test/resources/test.properties",
"chars": 305,
"preview": "test={{ vault.lookup('kv/key', 'value') }}\n\ntest2={{ vault.lookupV2('kv2/key').get('value') }}\n\ntest3={{ contextkey }}\n\n"
},
{
"path": "src/test/resources/vault-crd.yaml",
"chars": 2098,
"preview": "apiVersion: apiextensions.k8s.io/v1beta1\nkind: CustomResourceDefinition\nmetadata:\n name: vault.koudingspawn.de\nspec:\n "
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the DaspawnW/vault-crd GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 110 files (274.3 KB), approximately 68.9k tokens, and a symbol index with 448 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.