├── .gitignore ├── helm ├── vault-csi.openshift.yaml ├── csi.openshift.yaml └── vault.openshift.yaml ├── scripts ├── argocd.hcl ├── vault-admin.hcl └── k8s-auth-method.sh ├── vault-config ├── argocd.yaml ├── vault-admin.yaml └── install │ ├── namespace.yaml │ └── vault.yaml ├── data └── expense.json ├── database ├── secret.yaml └── deployment.yaml ├── argocd ├── install │ ├── gitops.yaml │ └── argocd.yaml ├── expense.yaml ├── secrets.yaml ├── database.yaml └── project.yaml ├── expense ├── service.yaml ├── csi │ ├── service.yaml │ └── deployment-csi.yaml └── deployment.yaml ├── argocd-vault-plugin └── Dockerfile ├── secrets ├── database.yaml └── expense.yaml ├── Makefile ├── README.md └── LICENSE.md /.gitignore: -------------------------------------------------------------------------------- 1 | unseal.json 2 | secrets.env 3 | crc-login 4 | argocd-login -------------------------------------------------------------------------------- /helm/vault-csi.openshift.yaml: -------------------------------------------------------------------------------- 1 | csi: 2 | enabled: true 3 | debug: true -------------------------------------------------------------------------------- /scripts/argocd.hcl: -------------------------------------------------------------------------------- 1 | path "expense/static/*" { 2 | capabilities = ["read"] 3 | } -------------------------------------------------------------------------------- /helm/csi.openshift.yaml: -------------------------------------------------------------------------------- 1 | syncSecret: 2 | enabled: true 3 | 4 | enableSecretRotation: true -------------------------------------------------------------------------------- /vault-config/argocd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: argocd 6 | automountServiceAccountToken: true 7 | -------------------------------------------------------------------------------- /vault-config/vault-admin.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: vault-admin 6 | automountServiceAccountToken: true 7 | -------------------------------------------------------------------------------- /data/expense.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gas", 3 | "tripId": "d7fd4bf6-aeb9-45a0-b671-85dfc4d095aa", 4 | "cost": 60.78, 5 | "currency": "US", 6 | "date": "2020-01-03", 7 | "reimbursable": true 8 | } -------------------------------------------------------------------------------- /database/secret.yaml: -------------------------------------------------------------------------------- 1 | kind: Secret 2 | apiVersion: v1 3 | metadata: 4 | name: expense-db-mysql 5 | annotations: 6 | avp.kubernetes.io/path: "expense/static/mysql" 7 | avp.kubernetes.io/kv-version: "1" 8 | type: Opaque 9 | stringData: 10 | password: -------------------------------------------------------------------------------- /argocd/install/gitops.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: openshift-gitops-operator 5 | namespace: openshift-operators 6 | spec: 7 | channel: stable 8 | installPlanApproval: Automatic 9 | name: openshift-gitops-operator 10 | source: redhat-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /argocd/expense.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: expense 5 | namespace: openshift-gitops 6 | spec: 7 | destination: 8 | namespace: expenses 9 | server: https://kubernetes.default.svc 10 | project: expense-report 11 | source: 12 | path: expense 13 | repoURL: https://github.com/joatmon08/vault-argocd.git 14 | targetRevision: main 15 | syncPolicy: 16 | automated: 17 | prune: true 18 | selfHeal: true 19 | -------------------------------------------------------------------------------- /argocd/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: expense-secrets 5 | namespace: openshift-gitops 6 | spec: 7 | destination: 8 | namespace: expenses 9 | server: https://kubernetes.default.svc 10 | project: expense-report 11 | source: 12 | path: secrets 13 | repoURL: https://github.com/joatmon08/vault-argocd.git 14 | targetRevision: main 15 | syncPolicy: 16 | automated: 17 | prune: true 18 | selfHeal: true -------------------------------------------------------------------------------- /expense/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: expense 6 | labels: 7 | app: expense 8 | annotations: 9 | argocd.argoproj.io/sync-wave: "3" 10 | spec: 11 | selector: 12 | app: expense 13 | ports: 14 | - name: http 15 | protocol: TCP 16 | port: 5001 17 | targetPort: 5001 18 | --- 19 | apiVersion: v1 20 | kind: ServiceAccount 21 | metadata: 22 | name: expense 23 | annotations: 24 | argocd.argoproj.io/sync-wave: "3" 25 | automountServiceAccountToken: true -------------------------------------------------------------------------------- /vault-config/install/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: namespace-configuration-operator 5 | namespace: namespace-configuration-operator 6 | spec: 7 | channel: alpha 8 | installPlanApproval: Automatic 9 | name: namespace-configuration-operator 10 | source: community-operators 11 | sourceNamespace: openshift-marketplace 12 | --- 13 | apiVersion: operators.coreos.com/v1 14 | kind: OperatorGroup 15 | metadata: 16 | name: namespace-configuration-operator 17 | namespace: namespace-configuration-operator 18 | spec: 19 | targetNamespaces: [] -------------------------------------------------------------------------------- /vault-config/install/vault.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: vault-config-operator 5 | namespace: vault-config-operator 6 | spec: 7 | channel: alpha 8 | installPlanApproval: Automatic 9 | name: vault-config-operator 10 | source: community-operators 11 | sourceNamespace: openshift-marketplace 12 | config: 13 | env: 14 | - name: VAULT_ADDR 15 | value: "http://vault.vault:8200" 16 | - name: VAULT_TOKEN 17 | valueFrom: 18 | secretKeyRef: 19 | name: vault 20 | key: VAULT_TOKEN 21 | --- 22 | apiVersion: operators.coreos.com/v1 23 | kind: OperatorGroup 24 | metadata: 25 | name: vault-config-operator 26 | namespace: vault-config-operator 27 | spec: 28 | targetNamespaces: [] -------------------------------------------------------------------------------- /argocd/database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: expense-db 5 | namespace: openshift-gitops 6 | spec: 7 | destination: 8 | namespace: expenses 9 | server: https://kubernetes.default.svc 10 | project: expense-report 11 | source: 12 | path: database 13 | repoURL: https://github.com/joatmon08/vault-argocd.git 14 | targetRevision: main 15 | plugin: 16 | env: 17 | - name: VAULT_ADDR 18 | value: 'http://vault.vault.svc:8200' 19 | - name: AVP_K8S_ROLE 20 | value: argocd 21 | - name: AVP_TYPE 22 | value: vault 23 | - name: AVP_AUTH_TYPE 24 | value: k8s 25 | name: argocd-vault-plugin 26 | syncPolicy: 27 | automated: 28 | prune: true 29 | selfHeal: true 30 | -------------------------------------------------------------------------------- /argocd-vault-plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM argoproj/argocd:latest 2 | 3 | # Switch to root for the ability to perform install 4 | USER root 5 | 6 | # Install tools needed for your repo-server to retrieve & decrypt secrets, render manifests 7 | # (e.g. curl, awscli, gpg, sops) 8 | RUN apt-get update && \ 9 | apt-get install -y \ 10 | curl \ 11 | awscli \ 12 | gpg && \ 13 | apt-get clean && \ 14 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 15 | 16 | # Install the AVP plugin (as root so we can copy to /usr/local/bin) 17 | ENV AVP_VERSION=1.8.0 18 | ENV BIN=argocd-vault-plugin 19 | RUN curl -L -o ${BIN} https://github.com/IBM/argocd-vault-plugin/releases/download/v${AVP_VERSION}/argocd-vault-plugin_${AVP_VERSION}_linux_amd64 20 | RUN chmod +x ${BIN} 21 | RUN mv ${BIN} /usr/local/bin 22 | 23 | # Switch back to non-root user 24 | USER argocd -------------------------------------------------------------------------------- /scripts/vault-admin.hcl: -------------------------------------------------------------------------------- 1 | path "expense/*" { 2 | capabilities = ["create", "read", "update", "delete", "list"] 3 | } 4 | 5 | path "sys/mounts/*" { 6 | capabilities = ["create", "read", "update", "delete", "list", "sudo"] 7 | } 8 | 9 | path "sys/mounts" { 10 | capabilities = ["read"] 11 | } 12 | 13 | path "sys/auth" { 14 | capabilities = ["read"] 15 | } 16 | 17 | path "auth/kubernetes/*" { 18 | capabilities = ["create", "update", "read", "delete", "sudo"] 19 | } 20 | 21 | path "sys/policy/expense" { 22 | capabilities = ["create", "update", "read", "delete"] 23 | } 24 | 25 | path "sys/policies/acl/*" { 26 | capabilities = ["create", "read", "update", "delete", "list", "sudo"] 27 | } 28 | 29 | path "sys/policies/acl" { 30 | capabilities = ["read"] 31 | } 32 | 33 | path "sys/policies/password/*" { 34 | capabilities = ["create", "read", "update", "delete", "list", "sudo"] 35 | } 36 | 37 | path "sys/policies/password" { 38 | capabilities = ["read"] 39 | } -------------------------------------------------------------------------------- /argocd/project.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: AppProject 3 | metadata: 4 | name: expense-report 5 | namespace: openshift-gitops 6 | finalizers: 7 | - resources-finalizer.argocd.argoproj.io 8 | spec: 9 | description: Expense report application 10 | sourceRepos: 11 | - 'https://github.com/joatmon08/vault-argocd.git' 12 | destinations: 13 | - namespace: expenses 14 | server: https://kubernetes.default.svc 15 | 16 | # Deny all cluster-scoped resources from being created, except for Namespace 17 | clusterResourceWhitelist: 18 | - group: '' 19 | kind: Namespace 20 | 21 | # Allow all namespaced-scoped resources to be created, except for ResourceQuota, LimitRange, NetworkPolicy 22 | namespaceResourceBlacklist: 23 | - group: '' 24 | kind: ResourceQuota 25 | - group: '' 26 | kind: LimitRange 27 | - group: '' 28 | kind: NetworkPolicy 29 | 30 | # Enables namespace orphaned resource monitoring. 31 | orphanedResources: 32 | warn: false -------------------------------------------------------------------------------- /helm/vault.openshift.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | openshift: true 3 | 4 | injector: 5 | image: 6 | repository: "registry.connect.redhat.com/hashicorp/vault-k8s" 7 | tag: "0.14.1-ubi" 8 | 9 | agentImage: 10 | repository: "registry.connect.redhat.com/hashicorp/vault" 11 | tag: "1.9.0-ubi" 12 | 13 | server: 14 | route: 15 | enabled: true 16 | host: vault-vault.apps-crc.testing 17 | tls: 18 | termination: edge 19 | insecureEdgeTerminationPolicy: Allow 20 | 21 | image: 22 | repository: "registry.connect.redhat.com/hashicorp/vault" 23 | tag: "1.9.0-ubi" 24 | 25 | ha: 26 | enabled: true 27 | replicas: 1 28 | raft: 29 | enabled: true 30 | config: | 31 | ui = true 32 | listener "tcp" { 33 | tls_disable = 1 34 | address = "[::]:8200" 35 | cluster_address = "[::]:8201" 36 | } 37 | storage "raft" { 38 | path = "/vault/data" 39 | } 40 | service_registration "kubernetes" {} -------------------------------------------------------------------------------- /expense/csi/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: security.openshift.io/v1 3 | kind: SecurityContextConstraints 4 | metadata: 5 | name: vault-csi-provider 6 | allowPrivilegedContainer: false 7 | allowHostDirVolumePlugin: true 8 | allowHostNetwork: true 9 | allowHostPorts: true 10 | allowHostIPC: false 11 | allowHostPID: false 12 | readOnlyRootFilesystem: false 13 | defaultAddCapabilities: 14 | - SYS_ADMIN 15 | runAsUser: 16 | type: RunAsAny 17 | seLinuxContext: 18 | type: RunAsAny 19 | fsGroup: 20 | type: RunAsAny 21 | users: 22 | - system:serviceaccount:expenses:expense 23 | --- 24 | apiVersion: v1 25 | kind: Service 26 | metadata: 27 | name: expense 28 | labels: 29 | app: expense 30 | annotations: 31 | argocd.argoproj.io/sync-wave: "3" 32 | spec: 33 | selector: 34 | app: expense 35 | ports: 36 | - name: http 37 | protocol: TCP 38 | port: 5001 39 | targetPort: 5001 40 | --- 41 | apiVersion: v1 42 | kind: ServiceAccount 43 | metadata: 44 | name: expense 45 | annotations: 46 | argocd.argoproj.io/sync-wave: "3" 47 | automountServiceAccountToken: true -------------------------------------------------------------------------------- /scripts/k8s-auth-method.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | vault auth enable kubernetes 4 | 5 | KUBERNETES_SA_TOKEN_NAME=$(kubectl get serviceaccounts -n vault vault -o json | jq -r '.secrets[] | select(.name | test("-token-")).name') 6 | KUBERNETES_SA_TOKEN_VALUE=$(kubectl get secrets -n vault ${KUBERNETES_SA_TOKEN_NAME} -o jsonpath='{.data.token}' | base64 -d) 7 | KUBERNETES_SA_CA_CERT=$(kubectl get secrets -n vault ${KUBERNETES_SA_TOKEN_NAME} -o jsonpath="{.data['ca\.crt']}" | base64 -d) 8 | KUBERNETES_PORT_443_TCP_ADDR=$(kubectl config view -o jsonpath='{.clusters[].cluster.server}') 9 | 10 | vault write auth/kubernetes/config issuer="" \ 11 | token_reviewer_jwt="${KUBERNETES_SA_TOKEN_VALUE}" \ 12 | kubernetes_host="${KUBERNETES_PORT_443_TCP_ADDR}" \ 13 | kubernetes_ca_cert="${KUBERNETES_SA_CA_CERT}" 14 | 15 | ## Set up vault-admin for Vault Config operator 16 | vault policy write vault-admin scripts/vault-admin.hcl 17 | 18 | vault write auth/kubernetes/role/vault-admin \ 19 | bound_service_account_names=vault-admin \ 20 | bound_service_account_namespaces=expenses \ 21 | policies=vault-admin ttl=1h 22 | 23 | kubectl apply --namespace expenses -f vault-config/vault-admin.yaml 24 | 25 | 26 | ## Set up argocd-plugin for ArgoCD Vault plugin 27 | vault policy write argocd scripts/argocd.hcl 28 | 29 | vault write auth/kubernetes/role/argocd \ 30 | bound_service_account_names=argocd \ 31 | bound_service_account_namespaces=openshift-gitops \ 32 | policies=argocd ttl=1h 33 | 34 | kubectl apply --namespace openshift-gitops -f vault-config/argocd.yaml -------------------------------------------------------------------------------- /secrets/database.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: redhatcop.redhat.io/v1alpha1 3 | kind: SecretEngineMount 4 | metadata: 5 | name: static 6 | annotations: 7 | argocd.argoproj.io/sync-wave: "0" 8 | spec: 9 | authentication: 10 | path: kubernetes 11 | role: vault-admin 12 | serviceAccount: 13 | name: vault-admin 14 | type: kv 15 | path: expense 16 | --- 17 | apiVersion: redhatcop.redhat.io/v1alpha1 18 | kind: PasswordPolicy 19 | metadata: 20 | name: postgresql-password-policy 21 | annotations: 22 | argocd.argoproj.io/sync-wave: "0" 23 | spec: 24 | authentication: 25 | path: kubernetes 26 | role: vault-admin 27 | serviceAccount: 28 | name: vault-admin 29 | passwordPolicy: | 30 | length = 12 31 | rule "charset" { 32 | charset = "abcdefghijklmnopqrstuvwxyz" 33 | } 34 | rule "charset" { 35 | charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 36 | min-chars = 1 37 | } 38 | rule "charset" { 39 | charset = "0123456789" 40 | min-chars = 1 41 | } 42 | rule "charset" { 43 | charset = "!@" 44 | min-chars = 1 45 | } 46 | --- 47 | apiVersion: redhatcop.redhat.io/v1alpha1 48 | kind: RandomSecret 49 | metadata: 50 | name: mysql 51 | annotations: 52 | argocd.argoproj.io/sync-wave: "0" 53 | spec: 54 | authentication: 55 | path: kubernetes 56 | role: vault-admin 57 | serviceAccount: 58 | name: vault-admin 59 | path: expense/static 60 | secretKey: password 61 | secretFormat: 62 | passwordPolicyName: postgresql-password-policy 63 | refreshPeriod: 5m -------------------------------------------------------------------------------- /database/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: expense-db-mysql 6 | annotations: 7 | argocd.argoproj.io/sync-wave: "1" 8 | labels: 9 | app: expense-db-mysql 10 | framework: mysql 11 | spec: 12 | selector: 13 | app: expense-db-mysql 14 | framework: mysql 15 | ports: 16 | - name: http 17 | protocol: TCP 18 | port: 3306 19 | targetPort: 3306 20 | --- 21 | apiVersion: v1 22 | kind: ServiceAccount 23 | metadata: 24 | name: expense-db-mysql 25 | annotations: 26 | argocd.argoproj.io/sync-wave: "1" 27 | automountServiceAccountToken: true 28 | --- 29 | apiVersion: apps/v1 30 | kind: Deployment 31 | metadata: 32 | name: expense-db-mysql 33 | annotations: 34 | argocd.argoproj.io/sync-wave: "1" 35 | labels: 36 | app: expense-db-mysql 37 | spec: 38 | replicas: 1 39 | selector: 40 | matchLabels: 41 | app: expense-db-mysql 42 | template: 43 | metadata: 44 | labels: 45 | app: expense-db-mysql 46 | framework: mysql 47 | spec: 48 | serviceAccountName: expense-db-mysql 49 | containers: 50 | - name: expense-db-mysql 51 | image: "joatmon08/expense-db:mysql-8" 52 | ports: 53 | - containerPort: 3306 54 | env: 55 | - name: MYSQL_ROOT_PASSWORD 56 | valueFrom: 57 | secretKeyRef: 58 | name: expense-db-mysql 59 | key: password 60 | livenessProbe: 61 | tcpSocket: 62 | port: 3306 63 | initialDelaySeconds: 30 64 | periodSeconds: 60 -------------------------------------------------------------------------------- /expense/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: expense 6 | labels: 7 | app: expense 8 | annotations: 9 | argocd.argoproj.io/sync-wave: "3" 10 | spec: 11 | replicas: 1 12 | selector: 13 | matchLabels: 14 | app: expense 15 | template: 16 | metadata: 17 | annotations: 18 | vault.hashicorp.com/agent-inject: "true" 19 | vault.hashicorp.com/role: "expense" 20 | vault.hashicorp.com/agent-inject-secret-application.properties: "expense/database/mysql/creds/expense" 21 | vault.hashicorp.com/secret-volume-path-application.properties: "/tmp/config" 22 | vault.hashicorp.com/agent-inject-template-application.properties: | 23 | spring.zipkin.enabled=false 24 | spring.datasource.url=jdbc:mysql://expense-db-mysql.expenses:3306/DemoExpenses 25 | server.port=5001 26 | {{ with secret "expense/database/mysql/creds/expense" -}} 27 | spring.datasource.username={{ .Data.username }} 28 | spring.datasource.password={{ .Data.password }} 29 | {{- end }} 30 | labels: 31 | app: expense 32 | framework: java 33 | spec: 34 | serviceAccountName: expense 35 | containers: 36 | - name: expense 37 | image: "joatmon08/expense:java-v2" 38 | ports: 39 | - containerPort: 5001 40 | env: 41 | - name: SPRING_CONFIG_LOCATION 42 | value: "/tmp/config/application.properties" 43 | livenessProbe: 44 | httpGet: 45 | path: /api 46 | port: 5001 47 | initialDelaySeconds: 5 48 | periodSeconds: 10 -------------------------------------------------------------------------------- /secrets/expense.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: redhatcop.redhat.io/v1alpha1 3 | kind: SecretEngineMount 4 | metadata: 5 | name: mysql 6 | annotations: 7 | argocd.argoproj.io/sync-wave: "2" 8 | spec: 9 | authentication: 10 | path: kubernetes 11 | role: vault-admin 12 | serviceAccount: 13 | name: vault-admin 14 | type: database 15 | path: expense/database 16 | --- 17 | apiVersion: redhatcop.redhat.io/v1alpha1 18 | kind: DatabaseSecretEngineConfig 19 | metadata: 20 | name: mysql 21 | annotations: 22 | argocd.argoproj.io/sync-wave: "2" 23 | spec: 24 | authentication: 25 | path: kubernetes 26 | role: vault-admin 27 | serviceAccount: 28 | name: vault-admin 29 | pluginName: mysql-database-plugin 30 | allowedRoles: 31 | - expense 32 | connectionURL: '{{username}}:{{password}}@tcp(expense-db-mysql.expenses:3306)/' 33 | username: root 34 | rootCredentials: 35 | randomSecret: 36 | name: mysql 37 | path: expense/database/mysql 38 | --- 39 | apiVersion: redhatcop.redhat.io/v1alpha1 40 | kind: DatabaseSecretEngineRole 41 | metadata: 42 | name: expense 43 | annotations: 44 | argocd.argoproj.io/sync-wave: "2" 45 | spec: 46 | authentication: 47 | path: kubernetes 48 | role: vault-admin 49 | serviceAccount: 50 | name: vault-admin 51 | path: expense/database/mysql 52 | dBName: mysql 53 | creationStatements: 54 | - CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT ALL PRIVILEGES ON DemoExpenses.expense_item TO '{{name}}'@'%'; 55 | --- 56 | apiVersion: redhatcop.redhat.io/v1alpha1 57 | kind: Policy 58 | metadata: 59 | name: expense 60 | annotations: 61 | argocd.argoproj.io/sync-wave: "2" 62 | spec: 63 | authentication: 64 | path: kubernetes 65 | role: vault-admin 66 | serviceAccount: 67 | name: vault-admin 68 | policy: | 69 | path "expense/database/mysql/creds/expense" { 70 | capabilities = ["read"] 71 | } 72 | --- 73 | apiVersion: redhatcop.redhat.io/v1alpha1 74 | kind: KubernetesAuthEngineRole 75 | metadata: 76 | name: expense 77 | annotations: 78 | argocd.argoproj.io/sync-wave: "2" 79 | spec: 80 | authentication: 81 | path: kubernetes 82 | role: vault-admin 83 | serviceAccount: 84 | name: vault-admin 85 | path: kubernetes 86 | policies: 87 | - expense 88 | targetServiceAccounts: 89 | - expense 90 | targetNamespaces: 91 | targetNamespaces: 92 | - expenses 93 | -------------------------------------------------------------------------------- /argocd/install/argocd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ArgoCD 3 | metadata: 4 | finalizers: 5 | - argoproj.io/finalizer 6 | name: openshift-gitops 7 | namespace: openshift-gitops 8 | spec: 9 | applicationSet: 10 | resources: 11 | limits: 12 | cpu: "2" 13 | memory: 1Gi 14 | requests: 15 | cpu: 250m 16 | memory: 512Mi 17 | configManagementPlugins: |- 18 | - name: argocd-vault-plugin 19 | generate: 20 | command: ["argocd-vault-plugin"] 21 | args: ["generate", "./"] 22 | controller: 23 | processors: {} 24 | resources: 25 | limits: 26 | cpu: "2" 27 | memory: 2Gi 28 | requests: 29 | cpu: 250m 30 | memory: 1Gi 31 | sharding: {} 32 | dex: 33 | openShiftOAuth: true 34 | resources: 35 | limits: 36 | cpu: 500m 37 | memory: 256Mi 38 | requests: 39 | cpu: 250m 40 | memory: 128Mi 41 | grafana: 42 | enabled: false 43 | ingress: 44 | enabled: false 45 | resources: 46 | limits: 47 | cpu: 500m 48 | memory: 256Mi 49 | requests: 50 | cpu: 250m 51 | memory: 128Mi 52 | route: 53 | enabled: false 54 | ha: 55 | enabled: false 56 | resources: 57 | limits: 58 | cpu: 500m 59 | memory: 256Mi 60 | requests: 61 | cpu: 250m 62 | memory: 128Mi 63 | initialSSHKnownHosts: {} 64 | prometheus: 65 | enabled: false 66 | ingress: 67 | enabled: false 68 | route: 69 | enabled: false 70 | rbac: 71 | defaultPolicy: 'role:readonly' 72 | policy: g, system:cluster-admins, role:admin 73 | scopes: '[groups]' 74 | redis: 75 | resources: 76 | limits: 77 | cpu: 500m 78 | memory: 256Mi 79 | requests: 80 | cpu: 250m 81 | memory: 128Mi 82 | repo: 83 | image: quay.io/joatmon080/argocd-vault-plugin 84 | version: "1.8.0" 85 | mountsatoken: true 86 | serviceaccount: "argocd" 87 | resources: 88 | limits: 89 | cpu: "1" 90 | memory: 1Gi 91 | requests: 92 | cpu: 250m 93 | memory: 256Mi 94 | resourceExclusions: | 95 | - apiGroups: 96 | - tekton.dev 97 | clusters: 98 | - '*' 99 | kinds: 100 | - TaskRun 101 | - PipelineRun 102 | server: 103 | autoscale: 104 | enabled: false 105 | grpc: 106 | ingress: 107 | enabled: false 108 | ingress: 109 | enabled: false 110 | resources: 111 | limits: 112 | cpu: 500m 113 | memory: 256Mi 114 | requests: 115 | cpu: 125m 116 | memory: 128Mi 117 | route: 118 | enabled: true 119 | service: 120 | type: "" 121 | tls: 122 | ca: {} -------------------------------------------------------------------------------- /expense/csi/deployment-csi.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: secrets-store.csi.x-k8s.io/v1 3 | kind: SecretProviderClass 4 | metadata: 5 | name: expense-db-login 6 | spec: 7 | provider: vault 8 | secretObjects: 9 | - secretName: expense-db-login 10 | type: Opaque 11 | data: 12 | - objectName: expense-db-username 13 | key: username 14 | - objectName: expense-db-password 15 | key: password 16 | parameters: 17 | roleName: 'expense' 18 | vaultAddress: 'http://vault.vault:8200' 19 | objects: | 20 | - objectName: "expense-db-username" 21 | secretPath: "expense/database/mysql/creds/expense" 22 | secretKey: "username" 23 | - objectName: "expense-db-password" 24 | secretPath: "expense/database/mysql/creds/expense" 25 | secretKey: "password" 26 | --- 27 | apiVersion: v1 28 | kind: ConfigMap 29 | metadata: 30 | name: expense 31 | data: 32 | application.properties: | 33 | spring.zipkin.enabled=false 34 | spring.datasource.url=jdbc:mysql://expense-db-mysql.expenses:3306/DemoExpenses 35 | server.port=5001 36 | spring.datasource.username=${MYSQL_DB_USERNAME} 37 | spring.datasource.password=${MYSQL_DB_PASSWORD} 38 | --- 39 | apiVersion: apps/v1 40 | kind: Deployment 41 | metadata: 42 | name: expense 43 | labels: 44 | app: expense 45 | spec: 46 | replicas: 1 47 | selector: 48 | matchLabels: 49 | app: expense 50 | template: 51 | metadata: 52 | labels: 53 | app: expense 54 | framework: java 55 | spec: 56 | serviceAccountName: expense 57 | containers: 58 | - name: expense 59 | image: "joatmon08/expense:java-v2" 60 | ports: 61 | - containerPort: 5001 62 | env: 63 | - name: MYSQL_DB_USERNAME 64 | valueFrom: 65 | secretKeyRef: 66 | name: expense-db-login 67 | key: username 68 | - name: MYSQL_DB_PASSWORD 69 | valueFrom: 70 | secretKeyRef: 71 | name: expense-db-login 72 | key: password 73 | - name: SPRING_CONFIG_LOCATION 74 | value: "/tmp/config/application.properties" 75 | volumeMounts: 76 | - name: vault 77 | mountPath: '/mnt/secrets-store' 78 | readOnly: true 79 | - name: config 80 | mountPath: '/tmp/config/' 81 | readOnly: true 82 | livenessProbe: 83 | httpGet: 84 | path: /api 85 | port: 5001 86 | initialDelaySeconds: 5 87 | periodSeconds: 10 88 | volumes: 89 | - name: vault 90 | csi: 91 | driver: 'secrets-store.csi.k8s.io' 92 | readOnly: true 93 | volumeAttributes: 94 | secretProviderClass: 'expense-db-login' 95 | - name: config 96 | configMap: 97 | name: expense -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export VAULT_TOKEN=$(shell cat unseal.json | jq -r '.root_token') 2 | export VAULT_ADDR=http://vault-vault.apps-crc.testing 3 | export ARGO_URL=$(shell oc get route openshift-gitops-server -n openshift-gitops -o jsonpath='{.spec.host}{"\n"}') 4 | export ARGO_PASSWORD=$(shell oc get secret/openshift-gitops-cluster -n openshift-gitops -o jsonpath='{.data.admin\.password}' | base64 -d) 5 | 6 | docker-build: 7 | cd argocd-vault-plugin && docker build -t quay.io/joatmon080/argocd-vault-plugin:1.8.0 . 8 | docker push quay.io/joatmon080/argocd-vault-plugin:1.8.0 9 | 10 | crc-start: 11 | crc setup 12 | crc start > crc-login 13 | oc login -u kubeadmin https://api.crc.testing:6443 14 | 15 | helm-setup: 16 | helm repo add hashicorp https://helm.releases.hashicorp.com 17 | helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts 18 | 19 | openshift-projects: 20 | oc new-project vault || true 21 | oc new-project expenses || true 22 | 23 | csi-deploy: 24 | helm upgrade --install --namespace=vault --version=1.1.1 --values=helm/csi.openshift.yaml csi secrets-store-csi-driver/secrets-store-csi-driver 25 | helm upgrade --install --namespace=vault --version=0.19.0 --values=helm/vault.openshift.yaml --values=helm/vault-csi.openshift.yaml vault hashicorp/vault 26 | kubectl patch --namespace=vault daemonset vault-csi-provider --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/securityContext", "value": {"privileged": true} }]' 27 | 28 | openshift-csi: csi-deploy 29 | oc adm policy add-scc-to-user privileged system:serviceaccount:vault:secrets-store-csi-driver 30 | oc adm policy add-scc-to-user privileged system:serviceaccount:vault:vault-csi-provider 31 | 32 | vault-init: 33 | kubectl exec -ti --namespace=vault vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > unseal.json 34 | kubectl exec -ti --namespace=vault vault-0 -- vault operator unseal 35 | 36 | openshift-gitops-deploy: 37 | oc apply -f argocd/install/gitops.yaml 38 | oc adm policy add-cluster-role-to-user cluster-admin system:serviceaccount:openshift-gitops:openshift-gitops-argocd-application-controller 39 | 40 | vault-auth-method: 41 | bash scripts/k8s-auth-method.sh 42 | oc delete -f argocd/install/argocd.yaml 43 | oc apply -f argocd/install/argocd.yaml 44 | 45 | vault-config-operator: 46 | oc new-project vault-config-operator || true 47 | kubectl create --save-config --dry-run=client secret generic vault \ 48 | --from-literal=VAULT_ADDR=http://vault.vault:8200 \ 49 | --from-literal=VAULT_TOKEN=$(shell cat unseal.json | jq -r '.root_token') \ 50 | -o yaml | kubectl apply --namespace vault-config-operator -f - 51 | kubectl apply -f vault-config/install/vault.yaml 52 | oc new-project namespace-configuration-operator || true 53 | kubectl apply -f vault-config/install/namespace.yaml 54 | 55 | db-secrets: 56 | argocd login --insecure --grpc-web ${ARGO_URL} --username admin --password ${ARGO_PASSWORD} 57 | kubectl apply -f argocd/project.yaml 58 | kubectl apply -f argocd/secrets.yaml 59 | 60 | db-deploy: 61 | kubectl apply -f argocd/database.yaml 62 | argocd app sync expense-secrets --replace 63 | 64 | app-deploy: 65 | kubectl apply -f argocd/expense.yaml 66 | 67 | expense-port-forward: 68 | kubectl port-forward --namespace=expenses svc/expense 15001:5001 69 | 70 | expense-test: 71 | curl -X POST 'http://localhost:15001/api/expense' -H 'Content-Type:application/json' -d @data/expense.json 72 | curl 'http://localhost:15001/api/expense' -H 'Content-Type:application/json' 73 | 74 | expense-csi: 75 | kubectl apply -f expense/csi/ -n expenses 76 | 77 | clean: 78 | crc delete -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Running Vault with ArgoCD 2 | 3 | ## Prerequisites 4 | 5 | - Helm 3.0+ 6 | - Vault 1.9+ 7 | 8 | ## Install Vault 9 | 10 | ### OpenShift 11 | 12 | This is an example! You can try it with Red Hat CodeReady Containers. 13 | 14 | 1. Install CRC. 15 | 16 | 1. Set up a cluster. Make sure to paste the pull secret when prompted. 17 | The command will log you in as an administrator. 18 | ```shell 19 | make crc-start 20 | ``` 21 | 22 | 1. Add the Secrets Store CSI driver and HashiCorp Helm repositories. 23 | ```shell 24 | make helm-setup 25 | ``` 26 | 27 | 1. Set up OpenShift projects for `vault` and the application (`expenses`). 28 | ```shell 29 | make openshift-projects 30 | ``` 31 | 32 | 1. Deploy the Secrets Store CSI driver and Vault Helm chart with OpenShift values. 33 | The values deploy a Vault cluster with one server (high availability configuration) 34 | and an injector. 35 | 36 | > Note: By default, HA mode [deploys 3 servers](https://www.vaultproject.io/docs/platform/k8s/helm/openshift#highly-available-raft-mode) 37 | > with a constraint of one server per unique host. 38 | > As a CRC cluster, we only have one host so I can only deploy one server. 39 | 40 | ```shell 41 | make openshift-csi 42 | ``` 43 | 44 | 1. Vault starts out uninitialized and sealed! This is to protect the secrets. 45 | You need to give it __one__ unseal keys in order to open Vault for use. 46 | Copy the unseal key from `unseal_keys_hex` in `unseal.json` 47 | 48 | > Note: Vault's seal mechanism uses [Shamir's secret sharing](https://www.vaultproject.io/docs/concepts/seal). 49 | > This is a manual process to secure the cluster if it restarts. You can use 50 | > [auto-unseal](https://www.vaultproject.io/docs/configuration/seal) for 51 | > specific cloud providers to bypass the manual requirement. 52 | 53 | ```shell 54 | make vault-init 55 | ``` 56 | 57 | ## Deploy ArgoCD 58 | 59 | We use Red Hat's [Openshift GitOps](https://docs.openshift.com/container-platform/4.9/cicd/gitops/understanding-openshift-gitops.html) 60 | to deploy ArgoCD to our cluster. 61 | 62 | 1. Deploy OpenShift GitOps into the `openshift-gitops` namespace. 63 | The command reinstalls ArgoCD with the `argocd-vault-plugin`. 64 | ```shell 65 | make openshift-gitops-deploy 66 | ``` 67 | 68 | ## Set up Kubernetes authentication method 69 | 70 | Vault uses the concept of authentication methods (AKA auth method) to allow an 71 | entity to retrieve a secret. 72 | [Authentication methods](https://www.vaultproject.io/docs/auth) are plugins that integrate 73 | with authentication providers, like OIDC or Kubernetes. 74 | 75 | We'll use the [Kubernetes auth method](https://www.vaultproject.io/docs/auth/kubernetes), 76 | which uses a service account identity to allow a pod 77 | to access a secret from Vault. 78 | 79 | The Kubernetes auth method attaches to two Vault roles. 80 | 81 | - `vault-admin`: for the `vault-config-operator` to configure secrets engines and policies 82 | - `argocd`: for the `argocd-vault-plugin` to read secrets for the expense application 83 | 84 | These two Vault roles ensure that you can audit and identify which entity accesses 85 | Vault and for what purposes. 86 | 87 | 1. Set up the Kubernetes authentication method. 88 | ```shell 89 | make vault-auth-method 90 | ``` 91 | 92 | The command replaces the `ArgoCD` specification with a customized one that... 93 | 94 | - Installs the `argocd-vault-plugin` 95 | - Uses the `argocd` service account 96 | 97 | ## Deploy the Vault configuration operator 98 | 99 | You can use Kubernetes manifests to configure Vault secrets engines 100 | and policies. In this example, you'll pass custom resources to configure 101 | KV and database secrets engines for the expense database and application. 102 | 103 | 1. Deploy the [Vault config operator](https://github.com/redhat-cop/vault-config-operator). 104 | ```shell 105 | make vault-config-operator 106 | ``` 107 | 108 | ## Configure secrets engines, database, and applications 109 | 110 | Set up a static secrets for the database root password using the 111 | Vault config operator. It will create password policy and random secret, 112 | stored in Vault's key-value store (version 1). 113 | 114 | > Note: `RandomSecret` may not work with kv version 2. 115 | 116 | 1. Set up the ArgoCD project and secrets engines in Vault. 117 | ```shell 118 | make db-secrets 119 | ``` 120 | 121 | 1. Deploy the database. This allows Vault to configure the database secrets engine. 122 | It uses the `argocd-vault-plugin` to inject secrets into the database. 123 | ```shell 124 | make db-deploy 125 | ``` 126 | 127 | 1. Deploy the application. It uses the database secrets engine set up by the Vault 128 | config operator. However, the application includes Vault agent instead of the 129 | `argocd-vault-plugin`. 130 | ```shell 131 | make app-deploy 132 | ``` 133 | 134 | ## Clean up 135 | 136 | ```shell 137 | crc delete 138 | ``` -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. “Contributor” 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. “Contributor Version” 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor’s Contribution. 14 | 15 | 1.3. “Contribution” 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. “Covered Software” 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. “Incompatible With Secondary Licenses” 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of version 33 | 1.1 or earlier of the License, but not also under the terms of a 34 | Secondary License. 35 | 36 | 1.6. “Executable Form” 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. “Larger Work” 41 | 42 | means a work that combines Covered Software with other material, in a separate 43 | file or files, that is not Covered Software. 44 | 45 | 1.8. “License” 46 | 47 | means this document. 48 | 49 | 1.9. “Licensable” 50 | 51 | means having the right to grant, to the maximum extent possible, whether at the 52 | time of the initial grant or subsequently, any and all of the rights conveyed by 53 | this License. 54 | 55 | 1.10. “Modifications” 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, deletion 60 | from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. “Patent Claims” of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, process, 67 | and apparatus claims, in any patent Licensable by such Contributor that 68 | would be infringed, but for the grant of the License, by the making, 69 | using, selling, offering for sale, having made, import, or transfer of 70 | either its Contributions or its Contributor Version. 71 | 72 | 1.12. “Secondary License” 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. “Source Code Form” 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. “You” (or “Your”) 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, “You” includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, “control” means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or as 104 | part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its Contributions 108 | or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution become 113 | effective for each Contribution on the date the Contributor first distributes 114 | such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under this 119 | License. No additional rights or licenses will be implied from the distribution 120 | or licensing of Covered Software under this License. Notwithstanding Section 121 | 2.1(b) above, no patent license is granted by a Contributor: 122 | 123 | a. for any code that a Contributor has removed from Covered Software; or 124 | 125 | b. for infringements caused by: (i) Your and any other third party’s 126 | modifications of Covered Software, or (ii) the combination of its 127 | Contributions with other software (except as part of its Contributor 128 | Version); or 129 | 130 | c. under Patent Claims infringed by Covered Software in the absence of its 131 | Contributions. 132 | 133 | This License does not grant any rights in the trademarks, service marks, or 134 | logos of any Contributor (except as may be necessary to comply with the 135 | notice requirements in Section 3.4). 136 | 137 | 2.4. Subsequent Licenses 138 | 139 | No Contributor makes additional grants as a result of Your choice to 140 | distribute the Covered Software under a subsequent version of this License 141 | (see Section 10.2) or under the terms of a Secondary License (if permitted 142 | under the terms of Section 3.3). 143 | 144 | 2.5. Representation 145 | 146 | Each Contributor represents that the Contributor believes its Contributions 147 | are its original creation(s) or it has sufficient rights to grant the 148 | rights to its Contributions conveyed by this License. 149 | 150 | 2.6. Fair Use 151 | 152 | This License is not intended to limit any rights You have under applicable 153 | copyright doctrines of fair use, fair dealing, or other equivalents. 154 | 155 | 2.7. Conditions 156 | 157 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 158 | Section 2.1. 159 | 160 | 161 | 3. Responsibilities 162 | 163 | 3.1. Distribution of Source Form 164 | 165 | All distribution of Covered Software in Source Code Form, including any 166 | Modifications that You create or to which You contribute, must be under the 167 | terms of this License. You must inform recipients that the Source Code Form 168 | of the Covered Software is governed by the terms of this License, and how 169 | they can obtain a copy of this License. You may not attempt to alter or 170 | restrict the recipients’ rights in the Source Code Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | a. such Covered Software must also be made available in Source Code Form, 177 | as described in Section 3.1, and You must inform recipients of the 178 | Executable Form how they can obtain a copy of such Source Code Form by 179 | reasonable means in a timely manner, at a charge no more than the cost 180 | of distribution to the recipient; and 181 | 182 | b. You may distribute such Executable Form under the terms of this License, 183 | or sublicense it under different terms, provided that the license for 184 | the Executable Form does not attempt to limit or alter the recipients’ 185 | rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for the 191 | Covered Software. If the Larger Work is a combination of Covered Software 192 | with a work governed by one or more Secondary Licenses, and the Covered 193 | Software is not Incompatible With Secondary Licenses, this License permits 194 | You to additionally distribute such Covered Software under the terms of 195 | such Secondary License(s), so that the recipient of the Larger Work may, at 196 | their option, further distribute the Covered Software under the terms of 197 | either this License or such Secondary License(s). 198 | 199 | 3.4. Notices 200 | 201 | You may not remove or alter the substance of any license notices (including 202 | copyright notices, patent notices, disclaimers of warranty, or limitations 203 | of liability) contained within the Source Code Form of the Covered 204 | Software, except that You may alter any license notices to the extent 205 | required to remedy known factual inaccuracies. 206 | 207 | 3.5. Application of Additional Terms 208 | 209 | You may choose to offer, and to charge a fee for, warranty, support, 210 | indemnity or liability obligations to one or more recipients of Covered 211 | Software. However, You may do so only on Your own behalf, and not on behalf 212 | of any Contributor. You must make it absolutely clear that any such 213 | warranty, support, indemnity, or liability obligation is offered by You 214 | alone, and You hereby agree to indemnify every Contributor for any 215 | liability incurred by such Contributor as a result of warranty, support, 216 | indemnity or liability terms You offer. You may include additional 217 | disclaimers of warranty and limitations of liability specific to any 218 | jurisdiction. 219 | 220 | 4. Inability to Comply Due to Statute or Regulation 221 | 222 | If it is impossible for You to comply with any of the terms of this License 223 | with respect to some or all of the Covered Software due to statute, judicial 224 | order, or regulation then You must: (a) comply with the terms of this License 225 | to the maximum extent possible; and (b) describe the limitations and the code 226 | they affect. Such description must be placed in a text file included with all 227 | distributions of the Covered Software under this License. Except to the 228 | extent prohibited by statute or regulation, such description must be 229 | sufficiently detailed for a recipient of ordinary skill to be able to 230 | understand it. 231 | 232 | 5. Termination 233 | 234 | 5.1. The rights granted under this License will terminate automatically if You 235 | fail to comply with any of its terms. However, if You become compliant, 236 | then the rights granted under this License from a particular Contributor 237 | are reinstated (a) provisionally, unless and until such Contributor 238 | explicitly and finally terminates Your grants, and (b) on an ongoing basis, 239 | if such Contributor fails to notify You of the non-compliance by some 240 | reasonable means prior to 60 days after You have come back into compliance. 241 | Moreover, Your grants from a particular Contributor are reinstated on an 242 | ongoing basis if such Contributor notifies You of the non-compliance by 243 | some reasonable means, this is the first time You have received notice of 244 | non-compliance with this License from such Contributor, and You become 245 | compliant prior to 30 days after Your receipt of the notice. 246 | 247 | 5.2. If You initiate litigation against any entity by asserting a patent 248 | infringement claim (excluding declaratory judgment actions, counter-claims, 249 | and cross-claims) alleging that a Contributor Version directly or 250 | indirectly infringes any patent, then the rights granted to You by any and 251 | all Contributors for the Covered Software under Section 2.1 of this License 252 | shall terminate. 253 | 254 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 255 | license agreements (excluding distributors and resellers) which have been 256 | validly granted by You or Your distributors under this License prior to 257 | termination shall survive termination. 258 | 259 | 6. Disclaimer of Warranty 260 | 261 | Covered Software is provided under this License on an “as is” basis, without 262 | warranty of any kind, either expressed, implied, or statutory, including, 263 | without limitation, warranties that the Covered Software is free of defects, 264 | merchantable, fit for a particular purpose or non-infringing. The entire 265 | risk as to the quality and performance of the Covered Software is with You. 266 | Should any Covered Software prove defective in any respect, You (not any 267 | Contributor) assume the cost of any necessary servicing, repair, or 268 | correction. This disclaimer of warranty constitutes an essential part of this 269 | License. No use of any Covered Software is authorized under this License 270 | except under this disclaimer. 271 | 272 | 7. Limitation of Liability 273 | 274 | Under no circumstances and under no legal theory, whether tort (including 275 | negligence), contract, or otherwise, shall any Contributor, or anyone who 276 | distributes Covered Software as permitted above, be liable to You for any 277 | direct, indirect, special, incidental, or consequential damages of any 278 | character including, without limitation, damages for lost profits, loss of 279 | goodwill, work stoppage, computer failure or malfunction, or any and all 280 | other commercial damages or losses, even if such party shall have been 281 | informed of the possibility of such damages. This limitation of liability 282 | shall not apply to liability for death or personal injury resulting from such 283 | party’s negligence to the extent applicable law prohibits such limitation. 284 | Some jurisdictions do not allow the exclusion or limitation of incidental or 285 | consequential damages, so this exclusion and limitation may not apply to You. 286 | 287 | 8. Litigation 288 | 289 | Any litigation relating to this License may be brought only in the courts of 290 | a jurisdiction where the defendant maintains its principal place of business 291 | and such litigation shall be governed by laws of that jurisdiction, without 292 | reference to its conflict-of-law provisions. Nothing in this Section shall 293 | prevent a party’s ability to bring cross-claims or counter-claims. 294 | 295 | 9. Miscellaneous 296 | 297 | This License represents the complete agreement concerning the subject matter 298 | hereof. If any provision of this License is held to be unenforceable, such 299 | provision shall be reformed only to the extent necessary to make it 300 | enforceable. Any law or regulation which provides that the language of a 301 | contract shall be construed against the drafter shall not be used to construe 302 | this License against a Contributor. 303 | 304 | 305 | 10. Versions of the License 306 | 307 | 10.1. New Versions 308 | 309 | Mozilla Foundation is the license steward. Except as provided in Section 310 | 10.3, no one other than the license steward has the right to modify or 311 | publish new versions of this License. Each version will be given a 312 | distinguishing version number. 313 | 314 | 10.2. Effect of New Versions 315 | 316 | You may distribute the Covered Software under the terms of the version of 317 | the License under which You originally received the Covered Software, or 318 | under the terms of any subsequent version published by the license 319 | steward. 320 | 321 | 10.3. Modified Versions 322 | 323 | If you create software not governed by this License, and you want to 324 | create a new license for such software, you may create and use a modified 325 | version of this License if you rename the license and remove any 326 | references to the name of the license steward (except to note that such 327 | modified license differs from this License). 328 | 329 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 330 | If You choose to distribute Source Code Form that is Incompatible With 331 | Secondary Licenses under the terms of this version of the License, the 332 | notice described in Exhibit B of this License must be attached. 333 | 334 | Exhibit A - Source Code Form License Notice 335 | 336 | This Source Code Form is subject to the 337 | terms of the Mozilla Public License, v. 338 | 2.0. If a copy of the MPL was not 339 | distributed with this file, You can 340 | obtain one at 341 | http://mozilla.org/MPL/2.0/. 342 | 343 | If it is not possible or desirable to put the notice in a particular file, then 344 | You may include the notice in a location (such as a LICENSE file in a relevant 345 | directory) where a recipient would be likely to look for such a notice. 346 | 347 | You may add additional accurate notices of copyright ownership. 348 | 349 | Exhibit B - “Incompatible With Secondary Licenses” Notice 350 | 351 | This Source Code Form is “Incompatible 352 | With Secondary Licenses”, as defined by 353 | the Mozilla Public License, v. 2.0. 354 | --------------------------------------------------------------------------------