-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# This is a combination of 20 commits.
# This is the 1st commit message: functional, permissions need to be modified # This is the commit message #2: bug fixes -- passing testing # This is the commit message #3: add README # This is the commit message #4: fix readme # This is the commit message #5: fix readme # This is the commit message #6: disable sync interval override # This is the commit message #7: add prerequisites to readme # This is the commit message #8: add more detail on create.sh compatibility # This is the commit message #9: add to create.sh to detect OS # This is the commit message #10: fix readme # This is the commit message #11: exit create.sh on any errors # This is the commit message #12: fix readme # This is the commit message #13: fix readme # This is the commit message #14: fix readme # This is the commit message #15: fix readme # This is the commit message #16: deletion works successfully # This is the commit message #17: fix readme # This is the commit message #18: fix readme # This is the commit message #19: fix readme # This is the commit message #20: fix readme
- Loading branch information
Showing
9 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
FROM centos:7 | ||
|
||
COPY yum.repo /etc/yum.repos.d/ | ||
|
||
RUN yum install -y kubectl | ||
|
||
COPY resource-syncer.py / | ||
|
||
CMD python -u /resource-syncer.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# k8s-secret-syncer | ||
|
||
## Overview | ||
|
||
In k8s, Secrets are only available to resources in the same namespace as the Secret itself. | ||
This means that if multiple namespaces need to use the same Secret, the only workaround is | ||
to maintain a copy of the Secret in each namespace that requires it. | ||
|
||
The *secret-syncer* tool is deployed into a k8s cluster to watch for any Secrets | ||
created in a given namespace and copy them to specified target namespaces, allowing | ||
one to maintain only a single copy of each Secret. | ||
|
||
## Installation | ||
|
||
#### Prerequisites | ||
- You have a GCP project with a k8s cluster that you can deploy resources to, as well as `gcloud` and `kubectl` configured to point to each, respectively. | ||
- Your docker client is [authenticated with GCR](https://cloud.google.com/sdk/gcloud/reference/auth/configure-docker). | ||
- Your k8s user has permissions to [create RBAC resources](https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control#prerequisites_for_using_role-based_access_control). | ||
|
||
#### Install *secret-syncer* | ||
To install *secret-syncer*, run the below script from this directory: | ||
```bash | ||
./create.sh | ||
``` | ||
This script uses `gcloud` and `kubectl` to build/push the container image for *secret-syncer* | ||
and create a Deployment `secret-syncer`, along with necessary RBAC resources, to the | ||
namespace `secrets` in the currently active k8s cluster. | ||
|
||
## Configuration | ||
|
||
*secret-syncer* can be configured with the below environment variables. | ||
|
||
Env Var | Description | Default | ||
--- | --- | --- | ||
SOURCE_NS | The namespace to copy secrets from | secrets | ||
SOURCE_ANNO | The annotation key to look for on secrets to determine the namespaces to it copy it to -- a secret will be copied to all namespaces that (regex) match its value for this annotation | ns-propagate | ||
NS_BLACKLIST | A comma-separated list of namespaces to ignore as destinations for copying -- note that SOURCE_NS is automatically appended to this| kube-system,kube-public,default | ||
SYNC_INTERVAL_SECONDS | The interval at which to look for and copy secrets | 300 | ||
SKIP_DELETE | By default, *secret-syncer* will delete secrets in any non-blacklisted namespaces that have the SOURCE_ANNO annotation but do not exist in the SOURCE_NS namespace -- set this to 'yes' to disable deletion | no | ||
RESOURCE_KIND | The kind of resources to copy (change at your own risk) | secret | ||
|
||
## Example Secret | ||
|
||
Below is an example secret that would be copied to all namespaces (excluding those in `NS_BLACKLIST`). | ||
|
||
```yaml | ||
apiVersion: v1 | ||
kind: Secret | ||
metadata: | ||
name: my-registry-credentials | ||
namespace: secrets | ||
annotations: | ||
ns-propagate: .* | ||
type: kubernetes.io/dockerconfigjson | ||
data: | ||
.dockerconfigjson: [REDACTED] | ||
``` | ||
## Notes | ||
- Secret annotations can be modified via `kubectl edit`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#!/bin/bash | ||
|
||
set -o errexit | ||
|
||
function replace_image() { | ||
local FILE=$1 | ||
local IMAGE=$2 | ||
[ "$(uname -a | cut -d ' ' -f 1)" == "Darwin" ] && BK='.bk' || BK='' # adjust for OS | ||
sed -i $BK "s~^\(.* image:\).*$~\1 $IMAGE~g" $FILE | ||
} | ||
|
||
IMAGE=gcr.io/$(gcloud config get-value project)/resource-syncer:0.1 | ||
docker build -t $IMAGE . && docker push $IMAGE | ||
|
||
# update image | ||
replace_image manifests/deployment.yaml $IMAGE | ||
|
||
# create ns, if DNE | ||
NS=secrets | ||
kubectl get ns $NS || kubectl create ns $NS | ||
|
||
# create resources | ||
kubectl apply -f manifests/rbac # create service account first | ||
kubectl apply -f manifests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
name: secret-syncer | ||
namespace: secrets | ||
spec: | ||
replicas: 1 | ||
selector: | ||
matchLabels: | ||
app: secret-syncer | ||
template: | ||
metadata: | ||
name: secret-syncer | ||
labels: | ||
app: secret-syncer | ||
spec: | ||
serviceAccountName: secret-syncer | ||
containers: | ||
- name: secret-syncer | ||
image: gcr.io/my-cool-gcp-project/resource-syncer:0.1 | ||
imagePullPolicy: Always | ||
#env: | ||
#- name: SYNC_INTERVAL_SECONDS | ||
# value: 10 | ||
resources: | ||
requests: | ||
cpu: 0.1 | ||
memory: 128M | ||
limits: | ||
cpu: 0.2 | ||
memory: 256M |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
kind: ClusterRole | ||
apiVersion: rbac.authorization.k8s.io/v1 | ||
metadata: | ||
name: secret-syncer | ||
rules: | ||
# read namespaces | ||
- apiGroups: [""] | ||
resources: ["namespaces"] | ||
verbs: ["get", "list"] | ||
# create/update secrets | ||
- apiGroups: [""] | ||
resources: ["secrets"] | ||
verbs: ["get", "list", "create", "update", "patch", "delete"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
apiVersion: rbac.authorization.k8s.io/v1 | ||
kind: ClusterRoleBinding | ||
metadata: | ||
name: secret-syncer | ||
roleRef: | ||
apiGroup: rbac.authorization.k8s.io | ||
kind: ClusterRole | ||
name: secret-syncer | ||
subjects: | ||
- kind: ServiceAccount | ||
name: secret-syncer | ||
namespace: secrets | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
apiVersion: v1 | ||
kind: ServiceAccount | ||
metadata: | ||
name: secret-syncer | ||
namespace: secrets |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import os | ||
import sys | ||
import json | ||
import subprocess | ||
import re | ||
import time | ||
import traceback | ||
|
||
# configuration | ||
RESOURCE_KIND = os.environ.get('RESOURCE_KIND', 'secret') # cluster role must be granted accordingly | ||
SYNC_INTERVAL_SECONDS = int(os.environ.get('SYNC_INTERVAL_SECONDS', 300)) | ||
SOURCE_NS = os.environ.get('SOURCE_NS', 'secrets') | ||
SOURCE_ANNO = os.environ.get('SOURCE_ANNO', 'ns-propagate') | ||
NS_BLACKLIST = os.environ.get('NS_BLACKLIST', 'kube-system,kube-public,default').split(',') | ||
SKIP_DELETE = os.environ.get('SKIP_DELETE', 'no') == 'yes' | ||
NS_BLACKLIST.append(SOURCE_NS) # don't copy back to source | ||
|
||
def kube_get(kind, namespace=None, name=None): | ||
cmd = 'kubectl get -o json ' + kind + (' -n ' + namespace if namespace else ' ') + (name if name else ' ') | ||
resp = subprocess.check_output(['/bin/sh', '-c', cmd]) | ||
return json.loads(resp)['items'] | ||
|
||
def kube_apply(definition): | ||
return subprocess.call(["/bin/sh", "-c", "echo '" + json.dumps(definition) + "' | kubectl apply -f -"]) | ||
|
||
def kube_delete(kind, namespace, name): | ||
print 'deleting %s %s in namespace %s' % (kind, name, namespace) | ||
return subprocess.call(["/bin/sh", "-c", "kubectl delete %s %s -n %s" % (kind, name, namespace)]) | ||
|
||
# modify a resource to strip out unique fields and switch the namespace | ||
def kube_switch_ns(definition, target_ns): | ||
definition['metadata']['namespace'] = target_ns | ||
for f in ('creationTimestamp', 'resourceVersion', 'uid'): | ||
if f in definition['metadata']: | ||
del definition['metadata'][f] | ||
|
||
# get all namespaces | ||
def kube_get_ns(): | ||
return [ns for ns in kube_get('namespace') if ns['metadata']['name'] not in NS_BLACKLIST] | ||
|
||
# get resources from source namespace | ||
def get_resources(kind, ns): | ||
resources = kube_get(kind, ns) | ||
return [r for r in resources if SOURCE_ANNO in r['metadata'].get('annotations', {})] | ||
|
||
# create resources in target namespaces | ||
def sync_resources(source_resources): | ||
namespaces = kube_get_ns() | ||
# create/update | ||
for r in source_resources: | ||
target_ns_pattern = r['metadata']['annotations'][SOURCE_ANNO] | ||
target_ns_list = [ns for ns in namespaces if re.match('^' + target_ns_pattern + '$', ns['metadata']['name'])] | ||
for ns in target_ns_list: | ||
print 'copying %s %s to namespace %s' % (RESOURCE_KIND, r['metadata']['name'], ns['metadata']['name']) | ||
kube_switch_ns(r, ns['metadata']['name']) | ||
if kube_apply(r) != 0: | ||
raise ValueError('error in copying ' + RESOURCE_KIND) | ||
# delete | ||
if not SKIP_DELETE: | ||
source_resources_list = [r['metadata']['name'] for r in source_resources] | ||
for ns in namespaces: | ||
current_resources_list = [r['metadata']['name'] for r in get_resources(RESOURCE_KIND, ns['metadata']['name'])] | ||
[kube_delete(RESOURCE_KIND, ns['metadata']['name'], r_name) for r_name in current_resources_list if r_name not in source_resources_list] | ||
|
||
# main | ||
print ''' | ||
starting resource syncer with the following properties: | ||
- syncing from source namespace "%s" | ||
- syncing resources of kind "%s" that include the annotation "%s" | ||
- omitting the following namespaces as sync destinations: "%s" | ||
''' % (SOURCE_NS, RESOURCE_KIND, SOURCE_ANNO, NS_BLACKLIST) | ||
|
||
while True: | ||
try: | ||
print '====== checking namespace %s for %ss to sync ======' % (SOURCE_NS, RESOURCE_KIND) | ||
resources = get_resources(RESOURCE_KIND, SOURCE_NS) | ||
sync_resources(resources) | ||
except Exception as e: | ||
traceback.print_exc() | ||
finally: | ||
time.sleep(SYNC_INTERVAL_SECONDS) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[kubernetes] | ||
name=Kubernetes | ||
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 | ||
enabled=1 | ||
gpgcheck=1 | ||
repo_gpgcheck=1 | ||
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg |