Merge pull request #998 from dod-ccpo/k8s-crls

K8s crls
This commit is contained in:
dandds 2019-08-06 13:31:37 -04:00 committed by GitHub
commit 326f8c3e03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 459 additions and 13 deletions

View File

@ -79,6 +79,7 @@ COPY --from=builder /install/config/ ./config/
COPY --from=builder /install/templates/ ./templates/
COPY --from=builder /install/translations.yaml .
COPY --from=builder /install/script/seed_roles.py ./script/seed_roles.py
COPY --from=builder /install/script/sync-crls ./script/sync-crls
COPY --from=builder /install/static/ ./static/
COPY --from=builder /install/uwsgi.ini .
COPY --from=builder /usr/local/bin/uwsgi /usr/local/bin/uwsgi

View File

@ -33,7 +33,7 @@ class CRLInterface:
def _log(self, message, level=logging.INFO):
if self.logger:
self.logger.log(level, message, extras={"tags": ["authorization", "crl"]})
self.logger.log(level, message, extra={"tags": ["authorization", "crl"]})
def crl_check(self, cert):
raise NotImplementedError()

View File

@ -0,0 +1,157 @@
import re
import os
import pendulum
import requests
class CRLNotFoundError(Exception):
pass
MODIFIED_TIME_BUFFER = 15 * 60
CRL_LIST = [
"http://crl.disa.mil/crl/DODROOTCA2.crl",
"http://crl.disa.mil/crl/DODROOTCA3.crl",
"http://crl.disa.mil/crl/DODROOTCA4.crl",
"http://crl.disa.mil/crl/DODROOTCA5.crl",
"http://crl.disa.mil/crl/DODIDCA_33.crl",
"http://crl.disa.mil/crl/DODIDCA_34.crl",
"http://crl.disa.mil/crl/DODIDSWCA_35.crl",
"http://crl.disa.mil/crl/DODIDSWCA_36.crl",
"http://crl.disa.mil/crl/DODIDSWCA_37.crl",
"http://crl.disa.mil/crl/DODIDSWCA_38.crl",
"http://crl.disa.mil/crl/DODIDCA_39.crl",
"http://crl.disa.mil/crl/DODIDCA_40.crl",
"http://crl.disa.mil/crl/DODIDCA_41.crl",
"http://crl.disa.mil/crl/DODIDCA_42.crl",
"http://crl.disa.mil/crl/DODIDCA_43.crl",
"http://crl.disa.mil/crl/DODIDCA_44.crl",
"http://crl.disa.mil/crl/DODIDSWCA_45.crl",
"http://crl.disa.mil/crl/DODIDSWCA_46.crl",
"http://crl.disa.mil/crl/DODIDSWCA_47.crl",
"http://crl.disa.mil/crl/DODIDSWCA_48.crl",
"http://crl.disa.mil/crl/DODIDCA_49.crl",
"http://crl.disa.mil/crl/DODIDCA_50.crl",
"http://crl.disa.mil/crl/DODIDCA_51.crl",
"http://crl.disa.mil/crl/DODIDCA_52.crl",
"http://crl.disa.mil/crl/DODIDCA_59.crl",
"http://crl.disa.mil/crl/DODSWCA_53.crl",
"http://crl.disa.mil/crl/DODSWCA_54.crl",
"http://crl.disa.mil/crl/DODSWCA_55.crl",
"http://crl.disa.mil/crl/DODSWCA_56.crl",
"http://crl.disa.mil/crl/DODSWCA_57.crl",
"http://crl.disa.mil/crl/DODSWCA_58.crl",
"http://crl.disa.mil/crl/DODSWCA_60.crl",
"http://crl.disa.mil/crl/DODSWCA_61.crl",
"http://crl.disa.mil/crl/DODEMAILCA_33.crl",
"http://crl.disa.mil/crl/DODEMAILCA_34.crl",
"http://crl.disa.mil/crl/DODEMAILCA_39.crl",
"http://crl.disa.mil/crl/DODEMAILCA_40.crl",
"http://crl.disa.mil/crl/DODEMAILCA_41.crl",
"http://crl.disa.mil/crl/DODEMAILCA_42.crl",
"http://crl.disa.mil/crl/DODEMAILCA_43.crl",
"http://crl.disa.mil/crl/DODEMAILCA_44.crl",
"http://crl.disa.mil/crl/DODEMAILCA_49.crl",
"http://crl.disa.mil/crl/DODEMAILCA_50.crl",
"http://crl.disa.mil/crl/DODEMAILCA_51.crl",
"http://crl.disa.mil/crl/DODEMAILCA_52.crl",
"http://crl.disa.mil/crl/DODEMAILCA_59.crl",
"http://crl.disa.mil/crl/DODINTEROPERABILITYROOTCA1.crl",
"http://crl.disa.mil/crl/DODINTEROPERABILITYROOTCA2.crl",
"http://crl.disa.mil/crl/USDODCCEBINTEROPERABILITYROOTCA1.crl",
"http://crl.disa.mil/crl/USDODCCEBINTEROPERABILITYROOTCA2.crl",
"http://crl.disa.mil/crl/DODNIPRINTERNALNPEROOTCA1.crl",
"http://crl.disa.mil/crl/DODNPEROOTCA1.crl",
"http://crl.disa.mil/crl/DMDNSIGNINGCA_1.crl",
"http://crl.disa.mil/crl/DODWCFROOTCA1.crl",
]
def crl_local_path(out_dir, crl_location):
name = re.split("/", crl_location)[-1]
crl = os.path.join(out_dir, name)
return crl
def existing_crl_modification_time(crl):
if os.path.exists(crl):
prev_time = os.path.getmtime(crl)
buffered = prev_time + MODIFIED_TIME_BUFFER
mod_time = prev_time if pendulum.now().timestamp() < buffered else buffered
dt = pendulum.from_timestamp(mod_time, tz="GMT")
return dt.format("ddd, DD MMM YYYY HH:mm:ss zz")
else:
return False
def write_crl(out_dir, target_dir, crl_location):
crl = crl_local_path(out_dir, crl_location)
existing = crl_local_path(target_dir, crl_location)
options = {"stream": True}
mod_time = existing_crl_modification_time(existing)
if mod_time:
options["headers"] = {"If-Modified-Since": mod_time}
with requests.get(crl_location, **options) as response:
if response.status_code > 399:
raise CRLNotFoundError()
if response.status_code == 304:
return False
with open(crl, "wb") as crl_file:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
crl_file.write(chunk)
return True
def remove_bad_crl(out_dir, crl_location):
crl = crl_local_path(out_dir, crl_location)
os.remove(crl)
def log_error(logger, crl_location):
if logger:
logger.error(
"Error downloading {}, removing file and continuing anyway".format(
crl_location
)
)
def refresh_crls(out_dir, target_dir, logger):
for crl_location in CRL_LIST:
logger.info("updating CRL from {}".format(crl_location))
try:
if write_crl(out_dir, target_dir, crl_location):
logger.info("successfully synced CRL from {}".format(crl_location))
else:
logger.info("no updates for CRL from {}".format(crl_location))
except requests.exceptions.ChunkedEncodingError:
log_error(logger, crl_location)
remove_bad_crl(out_dir, crl_location)
except CRLNotFoundError:
log_error(logger, crl_location)
if __name__ == "__main__":
import sys
import logging
logging.basicConfig(
level=logging.INFO, format="[%(asctime)s]:%(levelname)s: %(message)s"
)
logger = logging.getLogger()
logger.info("Updating CRLs")
try:
refresh_crls(sys.argv[1], sys.argv[2], logger)
except Exception as err:
logger.exception("Fatal error encountered, stopping")
sys.exit(1)
logger.info("Finished updating CRLs")

View File

@ -10,3 +10,4 @@ data:
OVERRIDE_CONFIG_FULLPATH: /opt/atat/atst/atst-overrides.ini
UWSGI_CONFIG_FULLPATH: /opt/atat/atst/uwsgi.ini
CRL_STORAGE_PROVIDER: CLOUDFILES
LOG_JSON: "true"

View File

@ -44,6 +44,8 @@ spec:
subPath: client-ca-bundle.pem
- name: uwsgi-socket-dir
mountPath: "/var/run/uwsgi"
- name: crls-vol
mountPath: "/opt/atat/atst/crls"
- name: nginx
image: nginx:alpine
ports:
@ -109,6 +111,9 @@ spec:
- key: tls.key
path: atat.key
mode: 0640
- name: crls-vol
persistentVolumeClaim:
claimName: efs
---
apiVersion: extensions/v1beta1
kind: Deployment

43
k8s/aws/crls-sync.yaml Normal file
View File

@ -0,0 +1,43 @@
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: crls
namespace: atat
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: crls
image: 904153757533.dkr.ecr.us-east-2.amazonaws.com/atat:8f1c8b5633ca70168837c885010e7d66d93562dc
command: [
"/bin/sh", "-c"
]
args: [
"/opt/atat/atst/script/sync-crls",
]
envFrom:
- configMapRef:
name: atst-envvars
- configMapRef:
name: atst-worker-envvars
volumeMounts:
- name: atst-config
mountPath: "/opt/atat/atst/atst-overrides.ini"
subPath: atst-overrides.ini
- name: crls-vol
mountPath: "/opt/atat/atst/crls"
volumes:
- name: atst-config
secret:
secretName: atst-config-ini
items:
- key: override.ini
path: atst-overrides.ini
mode: 0644
- name: crls-vol
persistentVolumeClaim:
claimName: efs

66
k8s/aws/efs-rbac.yml Normal file
View File

@ -0,0 +1,66 @@
# This can't be run without substituting the EFSID environment variable.
# from https://github.com/kubernetes-incubator/external-storage/blob/master/aws/efs/deploy/rbac.yaml
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: efs-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: efs-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete", "describe"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-efs-provisioner
subjects:
- kind: ServiceAccount
name: efs-provisioner
# replace with namespace where provisioner is deployed
namespace: atat
roleRef:
kind: ClusterRole
name: efs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-efs-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-efs-provisioner
subjects:
- kind: ServiceAccount
name: efs-provisioner
# replace with namespace where provisioner is deployed
namespace: atat
roleRef:
kind: Role
name: leader-locking-efs-provisioner
apiGroup: rbac.authorization.k8s.io

80
k8s/aws/storage-class.yml Normal file
View File

@ -0,0 +1,80 @@
# from https://github.com/kubernetes-incubator/external-storage/blob/master/aws/efs/deploy/manifest.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: efs-provisioner
data:
file.system.id: $EFSID
aws.region: us-east-2
provisioner.name: example.com/aws-efs
dns.name: $EFSID.efs.us-east-2.amazonaws.com
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: efs-provisioner
spec:
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: efs-provisioner
spec:
serviceAccountName: efs-provisioner
containers:
- name: efs-provisioner
image: quay.io/external_storage/efs-provisioner:latest
env:
- name: FILE_SYSTEM_ID
valueFrom:
configMapKeyRef:
name: efs-provisioner
key: file.system.id
- name: AWS_REGION
valueFrom:
configMapKeyRef:
name: efs-provisioner
key: aws.region
- name: DNS_NAME
valueFrom:
configMapKeyRef:
name: efs-provisioner
key: dns.name
optional: true
- name: PROVISIONER_NAME
valueFrom:
configMapKeyRef:
name: efs-provisioner
key: provisioner.name
volumeMounts:
- name: pv-volume
mountPath: /persistentvolumes
volumes:
- name: pv-volume
nfs:
server: $EFSID.efs.us-east-2.amazonaws.com
path: /
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: aws-efs
provisioner: example.com/aws-efs
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: efs
annotations:
volume.beta.kubernetes.io/storage-class: "aws-efs"
spec:
accessModes:
- ReadWriteMany
storageClassName: aws-efs
resources:
requests:
storage: 1Mi
---

View File

@ -10,3 +10,4 @@ data:
OVERRIDE_CONFIG_FULLPATH: /opt/atat/atst/atst-overrides.ini
UWSGI_CONFIG_FULLPATH: /opt/atat/atst/uwsgi.ini
CRL_STORAGE_PROVIDER: CLOUDFILES
LOG_JSON: "true"

View File

@ -44,6 +44,8 @@ spec:
subPath: client-ca-bundle.pem
- name: uwsgi-socket-dir
mountPath: "/var/run/uwsgi"
- name: crls-vol
mountPath: "/opt/atat/atst/crls"
- name: nginx
image: nginx:alpine
ports:
@ -109,6 +111,10 @@ spec:
- key: tls.key
path: atat.key
mode: 0640
- name: crls-vol
persistentVolumeClaim:
claimName: crls-vol-claim
---
apiVersion: extensions/v1beta1
kind: Deployment

43
k8s/azure/crls-sync.yaml Normal file
View File

@ -0,0 +1,43 @@
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: crls
namespace: atat
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: crls
image: pwatat.azurecr.io/atat:8f1c8b5633ca70168837c885010e7d66d93562dc
command: [
"/bin/sh", "-c"
]
args: [
"/opt/atat/atst/script/sync-crls",
]
envFrom:
- configMapRef:
name: atst-envvars
- configMapRef:
name: atst-worker-envvars
volumeMounts:
- name: atst-config
mountPath: "/opt/atat/atst/atst-overrides.ini"
subPath: atst-overrides.ini
- name: crls-vol
mountPath: "/opt/atat/atst/crls"
volumes:
- name: atst-config
secret:
secretName: atst-config-ini
items:
- key: override.ini
path: atst-overrides.ini
mode: 0644
- name: crls-vol
persistentVolumeClaim:
claimName: crls-vol-claim

View File

@ -0,0 +1,46 @@
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: azurefile
provisioner: kubernetes.io/azure-file
mountOptions:
- dir_mode=0777
- file_mode=0777
- uid=1000
- gid=1000
parameters:
skuName: Standard_LRS
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:azure-cloud-provider
rules:
- apiGroups: ['']
resources: ['secrets']
verbs: ['get','create']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:azure-cloud-provider
roleRef:
kind: ClusterRole
apiGroup: rbac.authorization.k8s.io
name: system:azure-cloud-provider
subjects:
- kind: ServiceAccount
name: persistent-volume-binder
namespace: kube-system
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: crls-vol-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: azurefile
resources:
requests:
storage: 1Gi

View File

@ -1,14 +1,11 @@
#! .venv/bin/python
# Add root application dir to the python path
import os
import sys
#!/bin/sh
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(parent_dir)
# script/sync-crls: update the DOD CRLs and place them where authnid expects them
set -e
cd "$(dirname "$0")/.."
from atst.app import make_config, make_app
if __name__ == "__main__":
config = make_config({"DISABLE_CRL_CHECK": True})
app = make_app(config)
app.csp.crls.sync_crls()
mkdir -p crl-tmp crls
# need to adjust this command
./.venv/bin/python ./atst/domain/authnid/crl/util.py crl-tmp crls
cp -r crl-tmp/* crls/
rm -rf crl-tmp