diff --git a/.gitignore b/.gitignore index 35d44eb4..212d1039 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,6 @@ ENV/ # Translation catalogs *.mo *.pot + +# Mac Files +.DS_Store diff --git a/Makefile b/Makefile index aa0c8057..b90810f1 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,13 @@ -.PHONY: dev prod run test +.PHONY: base dev prod run test + +base: requirements.txt + pip install -r requirements.txt dev: dev-requirements.txt pip install -r dev-requirements.txt -prod: requirements.txt - pip install -r requirements.txt +prod: prod-requirements.txt + pip install -r prod-requirements.txt run: prod FLASK_DEBUG=1 FLASK_APP=snappass.main NO_SSL=True venv/bin/flask run diff --git a/README.rst b/README.rst index 6e98da75..4522fd98 100644 --- a/README.rst +++ b/README.rst @@ -88,6 +88,8 @@ need to change this. ``REDIS_PORT``: is the port redis is serving on, defaults to 6379 +``REDIS_CA_CERTS``: useful when connecting to a Redis server that uses SSL. Example: ``"/path/to/ca.pem"`` + ``SNAPPASS_REDIS_DB``: is the database that you want to use on this redis server. Defaults to db 0 ``REDIS_URL``: (optional) will be used instead of ``REDIS_HOST``, ``REDIS_PORT``, and ``SNAPPASS_REDIS_DB`` to configure the Redis client object. For example: redis://username:password@localhost:6379/0 @@ -197,10 +199,10 @@ To check if a password exists, send a HEAD request to ``/api/v2/passwords/``, $ curl -X GET http://localhost:5000/api/v2/passwords/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D If : -- the token is valid +- the token is valid - the password : - exists - - has not been read + - has not been read - is not expired Then the API will return a 200 (OK) with a JSON response containing the password : @@ -286,6 +288,29 @@ Alternatively, you can use `Docker`_ and `Docker Compose`_ to install and run Sn This will pull all dependencies, i.e. Redis and appropriate Python version (3.7), then start up SnapPass and Redis server. SnapPass server is accessible at: http://localhost:5000 +Kubernetes +------ + +Alternatively, you can run SnapPass in a Kubernetes cluster. A sample deployment file is provided in the `kubernetes` directory. + +.. _Kubernetes: https://kubernetes.io/ + +Do the required changes in the `kubernetes/kustomize/overlays/dev` or `kubernetes/kustomize/overlays/prod` directory and then apply the changes using `kubectl`: + +:: + + $ kubectl kustomize kubernetes/kustomize/overlays/dev | kubectl apply -f - + +OR + +:: + + $ kubectl apply -f kubernetes/kustomize/overlays/prod | kubectl apply -f - + + +This will pull all dependencies, i.e. Redis and appropriate Python version (3.7), then start up SnapPass and Redis server. SnapPass server is accessible at: http://localhost:5000 + + Similar Tools ------------- diff --git a/kubernetes/kustomize/base/configmap.yaml b/kubernetes/kustomize/base/configmap.yaml new file mode 100644 index 00000000..33ec231a --- /dev/null +++ b/kubernetes/kustomize/base/configmap.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: snappass + namespace: snappass +data: + REDIS_HOST: KUSTOMIZE_HERE + REDIS_PORT: KUSTOMIZE_HERE + NO_SSL: "False" + SNAPPASS_REDIS_DB: "0" + REDIS_PREFIX: snappass + HOST_OVERRIDE: KUSTOMIZE_HERE diff --git a/kubernetes/kustomize/base/deployment.yaml b/kubernetes/kustomize/base/deployment.yaml new file mode 100644 index 00000000..6e3545ae --- /dev/null +++ b/kubernetes/kustomize/base/deployment.yaml @@ -0,0 +1,95 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: snappass + namespace: snappass +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + app.k8s.com/name: snappass + app.k8s.com/instance: snappass + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + spec: + containers: + - name: snappass + image: pinterest/snappass + imagePullPolicy: IfNotPresent + env: + - name: REDIS_HOST + valueFrom: + configMapKeyRef: + name: snappass + key: REDIS_HOST + - name: REDIS_PORT + valueFrom: + configMapKeyRef: + name: snappass + key: REDIS_PORT + - name: NO_SSL + valueFrom: + configMapKeyRef: + name: snappass + key: NO_SSL + - name: SNAPPASS_REDIS_DB + valueFrom: + configMapKeyRef: + name: snappass + key: SNAPPASS_REDIS_DB + - name: REDIS_PREFIX + valueFrom: + configMapKeyRef: + name: snappass + key: REDIS_PREFIX + - name: HOST_OVERRIDE + valueFrom: + configMapKeyRef: + name: snappass + key: HOST_OVERRIDE + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: snappass + key: SECRET_KEY + - name: REDIS_CA_CERTS + value: /etc/ssl/redis/redis.pem + ports: + - name: http + containerPort: 5000 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 60 + readinessProbe: + httpGet: + path: / + port: http + resources: + requests: + cpu: 500m + memory: 512Mi + volumeMounts: + - name: redis-ca-certs + mountPath: "/etc/ssl/redis" + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumes: + - name: redis-ca-certs + secret: + secretName: redis-ca-certs + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + serviceAccountName: snappass-ksa diff --git a/kubernetes/kustomize/base/ingress.yaml b/kubernetes/kustomize/base/ingress.yaml new file mode 100644 index 00000000..0e58a0ca --- /dev/null +++ b/kubernetes/kustomize/base/ingress.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: snappass-ingress + namespace: snappass +spec: + ingressClassName: nginx-ingress + rules: + - host: KUSTOMIZE_HERE + http: + paths: + - backend: + service: + name: snappass-http + port: + name: snappass-http + path: / + pathType: ImplementationSpecific + tls: + - hosts: + - KUSTOMIZE_HERE diff --git a/kubernetes/kustomize/base/kustomization.yaml b/kubernetes/kustomize/base/kustomization.yaml new file mode 100644 index 00000000..dc548523 --- /dev/null +++ b/kubernetes/kustomize/base/kustomization.yaml @@ -0,0 +1,11 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- namespace.yaml +- secret.yaml +- configmap.yaml +- serviceaccount.yaml +- deployment.yaml +- service.yaml +- ingress.yaml \ No newline at end of file diff --git a/kubernetes/kustomize/base/namespace.yaml b/kubernetes/kustomize/base/namespace.yaml new file mode 100644 index 00000000..d2784064 --- /dev/null +++ b/kubernetes/kustomize/base/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: snappass diff --git a/kubernetes/kustomize/base/secret.yaml b/kubernetes/kustomize/base/secret.yaml new file mode 100644 index 00000000..8e73e83f --- /dev/null +++ b/kubernetes/kustomize/base/secret.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: v1 +data: + redis.pem: KUSTOMIZE_HERE +kind: Secret +metadata: + name: redis-ca-certs \ No newline at end of file diff --git a/kubernetes/kustomize/base/service.yaml b/kubernetes/kustomize/base/service.yaml new file mode 100644 index 00000000..836cc096 --- /dev/null +++ b/kubernetes/kustomize/base/service.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: snappass-http + namespace: snappass +spec: + ports: + - name: snappass-http + port: 5000 + protocol: TCP + targetPort: 5000 + selector: + app.k8s.com/name: snappass + app.k8s.com/instance: snappass + sessionAffinity: None + type: ClusterIP diff --git a/kubernetes/kustomize/base/serviceaccount.yaml b/kubernetes/kustomize/base/serviceaccount.yaml new file mode 100644 index 00000000..699e1334 --- /dev/null +++ b/kubernetes/kustomize/base/serviceaccount.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: snappass-ksa + namespace: snappass diff --git a/kubernetes/kustomize/overlays/dev/kustomization.yaml b/kubernetes/kustomize/overlays/dev/kustomization.yaml new file mode 100644 index 00000000..ca0e8459 --- /dev/null +++ b/kubernetes/kustomize/overlays/dev/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +bases: +- ../../base + +# Add patches for configMap, Secret and Ingress +patches: [] diff --git a/kubernetes/kustomize/overlays/prod/kustomization.yaml b/kubernetes/kustomize/overlays/prod/kustomization.yaml new file mode 100644 index 00000000..ca5e2164 --- /dev/null +++ b/kubernetes/kustomize/overlays/prod/kustomization.yaml @@ -0,0 +1,22 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +bases: +- ../../base + +patches: +- patch: |- + - op: add + path: /spec/template/spec/containers/0/command + value: ["gunicorn"] + - op: add + path: /spec/template/spec/containers/0/args + value: + - snappass.main:app + - --bind=0.0.0.0:5000 + - --workers=4 + target: + kind: Deployment + name: snappass + +# Add patches for configMap, Secret and Ingress diff --git a/prod-requirements.txt b/prod-requirements.txt new file mode 100644 index 00000000..8a5f41dd --- /dev/null +++ b/prod-requirements.txt @@ -0,0 +1,9 @@ +cryptography==42.0.3 +Flask==3.0.0 +itsdangerous==2.1.2 +Jinja2==3.1.3 +MarkupSafe==2.1.1 +redis==5.0.1 +Werkzeug==3.0.3 +flask-babel +gunicorn==22.0.0 \ No newline at end of file diff --git a/snappass/main.py b/snappass/main.py index 6f06572e..280d6e76 100644 --- a/snappass/main.py +++ b/snappass/main.py @@ -17,6 +17,7 @@ URL_PREFIX = os.environ.get('URL_PREFIX', None) HOST_OVERRIDE = os.environ.get('HOST_OVERRIDE', None) TOKEN_SEPARATOR = '~' +REDIS_CA_CERTS = os.environ.get('REDIS_CA_CERTS', None) # Path to the CA PEM file # Initialize Flask Application app = Flask(__name__) @@ -45,8 +46,23 @@ def get_locale(): redis_host = os.environ.get('REDIS_HOST', 'localhost') redis_port = os.environ.get('REDIS_PORT', 6379) redis_db = os.environ.get('SNAPPASS_REDIS_DB', 0) - redis_client = redis.StrictRedis( - host=redis_host, port=redis_port, db=redis_db) + + # Use SSL/TLS if CA certs are provided + if REDIS_CA_CERTS: + redis_client = redis.StrictRedis( + host=redis_host, + port=redis_port, + db=redis_db, + ssl=True, + ssl_ca_certs=REDIS_CA_CERTS + ) + else: + redis_client = redis.StrictRedis( + host=redis_host, + port=redis_port, + db=redis_db + ) + REDIS_PREFIX = os.environ.get('REDIS_PREFIX', 'snappass') TIME_CONVERSION = {'two weeks': 1209600, 'week': 604800, 'day': 86400,