Add init script to download extra ca certs

This commit is contained in:
Sam Chow 2018-08-16 15:42:01 -04:00
parent 5b400f4c22
commit ff294d6c52
7 changed files with 112 additions and 17 deletions

View file

@ -0,0 +1,71 @@
import json
import os
import base64
from requests import Request, Session
QUAYPATH = os.environ.get('QUAYPATH', '.')
KUBE_EXTRA_CA_CERTDIR = os.environ.get('KUBE_EXTRA_CA_CERTDIR', '%s/conf/kube_extra_certs' % QUAYPATH)
KUBERNETES_API_HOST = os.environ.get('KUBERNETES_SERVICE_HOST', '')
port = os.environ.get('KUBERNETES_SERVICE_PORT')
if port:
KUBERNETES_API_HOST += ':' + port
SERVICE_ACCOUNT_TOKEN_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/token'
QE_NAMESPACE = os.environ.get('QE_K8S_NAMESPACE', 'quay-enterprise')
QE_CONFIG_SECRET = os.environ.get('QE_K8S_CONFIG_SECRET', 'quay-enterprise-config-secret')
EXTRA_CA_DIRECTORY_PREFIX = 'extra_ca_certs_'
def _lookup_secret(service_token):
secret_url = 'namespaces/%s/secrets/%s' % (QE_NAMESPACE, QE_CONFIG_SECRET)
response = _execute_k8s_api(service_token, 'GET', secret_url)
if response.status_code != 200:
raise Exception('Cannot get the config secret')
return json.loads(response.text)
def _execute_k8s_api(service_account_token, method, relative_url, data=None, api_prefix='api/v1', content_type='application/json'):
headers = {
'Authorization': 'Bearer ' + service_account_token
}
if data:
headers['Content-Type'] = content_type
data = json.dumps(data) if data else None
session = Session()
url = 'https://%s/%s/%s' % (KUBERNETES_API_HOST, api_prefix, relative_url)
request = Request(method, url, data=data, headers=headers)
return session.send(request.prepare(), verify=False, timeout=2)
def is_extra_cert(key):
return key.find(EXTRA_CA_DIRECTORY_PREFIX) == 0
def main():
# Load the service account token from the local store.
if not os.path.exists(SERVICE_ACCOUNT_TOKEN_PATH):
raise Exception('Cannot load Kubernetes service account token')
with open(SERVICE_ACCOUNT_TOKEN_PATH, 'r') as f:
service_token = f.read()
secret_data = _lookup_secret(service_token).get('data', {})
cert_keys = filter(is_extra_cert, secret_data.keys())
for cert_key in cert_keys:
if not os.path.exists(KUBE_EXTRA_CA_CERTDIR):
os.mkdir(KUBE_EXTRA_CA_CERTDIR)
cert_value = base64.b64decode(secret_data[cert_key])
cert_filename = cert_key.replace(EXTRA_CA_DIRECTORY_PREFIX, '')
print "Found an extra cert %s in config-secret, copying to kube ca dir"
with open(os.path.join(KUBE_EXTRA_CA_CERTDIR, cert_filename), 'w') as f:
f.write(cert_value)
if __name__ == '__main__':
main()

12
conf/init/02_get_kube_certs.sh Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
QUAYDIR=${QUAYDIR:-"/"}
QUAYPATH=${QUAYPATH:-"."}
QUAYCONF=${QUAYCONF:-"$QUAYPATH/conf"}
cd $QUAYDIR
if [[ "$KUBERNETES_SERVICE_HOST" != "" ]];then
echo "Running on kubernetes, attempting to retrieve extra certs from secret"
venv/bin/python $QUAYCONF/init/02_get_kube_certs.py
fi

View file

@ -3,6 +3,12 @@ set -e
QUAYPATH=${QUAYPATH:-"."} QUAYPATH=${QUAYPATH:-"."}
QUAYCONF=${QUAYCONF:-"$QUAYPATH/conf/stack"} QUAYCONF=${QUAYCONF:-"$QUAYPATH/conf/stack"}
QUAYCONFIG=${QUAYCONFIG:-"$QUAYCONF/stack"} QUAYCONFIG=${QUAYCONFIG:-"$QUAYCONF/stack"}
CERTDIR=${QUAYCONFIG/extra_ca_certs}
# If we're running under kube, the previous script (02_get_kube_certs.sh) will put the certs in a different location
if [[ "$KUBERNETES_SERVICE_HOST" != "" ]];then
CERTDIR=${KUBE_EXTRA_CA_CERTDIR:-"$QUAYPATH/conf/kube_extra_certs"}
fi
cd ${QUAYDIR:-"/quay-registry"} cd ${QUAYDIR:-"/quay-registry"}
@ -13,25 +19,25 @@ then
fi fi
# Add extra trusted certificates (as a directory) # Add extra trusted certificates (as a directory)
if [ -d $QUAYCONFIG/extra_ca_certs ]; then if [ -d $CERTDIR ]; then
if test "$(ls -A "$QUAYCONFIG/extra_ca_certs")"; then if test "$(ls -A "$CERTDIR")"; then
echo "Installing extra certificates found in $QUAYCONFIG/extra_ca_certs directory" echo "Installing extra certificates found in $CERTDIR directory"
cp $QUAYCONFIG/extra_ca_certs/* /usr/local/share/ca-certificates/ cp $CERTDIR/* /usr/local/share/ca-certificates/
cat $QUAYCONFIG/extra_ca_certs/* >> venv/lib/python2.7/site-packages/requests/cacert.pem cat $CERTDIR/* >> venv/lib/python2.7/site-packages/requests/cacert.pem
cat $QUAYCONFIG/extra_ca_certs/* >> venv/lib/python2.7/site-packages/certifi/cacert.pem cat $CERTDIR/* >> venv/lib/python2.7/site-packages/certifi/cacert.pem
fi fi
fi fi
# Add extra trusted certificates (as a file) # Add extra trusted certificates (as a file)
if [ -f $QUAYCONFIG/extra_ca_certs ]; then if [ -f $CERTDIR ]; then
echo "Installing extra certificates found in $QUAYCONFIG/extra_ca_certs file" echo "Installing extra certificates found in $CERTDIR file"
csplit -z -f /usr/local/share/ca-certificates/extra-ca- $QUAYCONFIG/extra_ca_certs '/-----BEGIN CERTIFICATE-----/' '{*}' csplit -z -f /usr/local/share/ca-certificates/extra-ca- $CERTDIR '/-----BEGIN CERTIFICATE-----/' '{*}'
cat $QUAYCONFIG/extra_ca_certs >> venv/lib/python2.7/site-packages/requests/cacert.pem cat $CERTDIR >> venv/lib/python2.7/site-packages/requests/cacert.pem
cat $QUAYCONFIG/extra_ca_certs >> venv/lib/python2.7/site-packages/certifi/cacert.pem cat $CERTDIR >> venv/lib/python2.7/site-packages/certifi/cacert.pem
fi fi
# Add extra trusted certificates (prefixed) # Add extra trusted certificates (prefixed)
for f in $(find $QUAYCONFIG/ -maxdepth 1 -type f -name "extra_ca*") for f in $(find $CERTDIR/ -maxdepth 1 -type f -name "extra_ca*")
do do
echo "Installing extra cert $f" echo "Installing extra cert $f"
cp "$f" /usr/local/share/ca-certificates/ cp "$f" /usr/local/share/ca-certificates/

View file

@ -10,6 +10,9 @@ proxy_redirect off;
proxy_set_header Transfer-Encoding $http_transfer_encoding; proxy_set_header Transfer-Encoding $http_transfer_encoding;
# The DB migrations sometimes take a while, so increase timeoutso we don't report an error
proxy_read_timeout 300s;
location / { location / {
proxy_pass http://web_app_server; proxy_pass http://web_app_server;
} }

View file

@ -54,8 +54,8 @@ class SuperUserCustomCertificate(ApiResource):
return '', 204 return '', 204
# Call the update script with config dir location to install the certificate immediately. # Call the update script with config dir location to install the certificate immediately.
if subprocess.call([os.path.join(INIT_SCRIPTS_LOCATION, 'certs_install.sh')], cert_dir = os.path.join(config_provider.get_config_dir_path(), EXTRA_CA_DIRECTORY)
env={ 'QUAYCONFIG': config_provider.get_config_dir_path() }) != 0: if subprocess.call([os.path.join(INIT_SCRIPTS_LOCATION, 'certs_install.sh')], env={ 'CERTDIR': cert_dir }) != 0:
raise Exception('Could not install certificates') raise Exception('Could not install certificates')
return '', 204 return '', 204

View file

@ -44,6 +44,10 @@ class TransientDirectoryProvider(FileConfigProvider):
def save_configuration_to_kubernetes(self): def save_configuration_to_kubernetes(self):
data = {} data = {}
# Kubernetes secrets don't have sub-directories, so for the extra_ca_certs dir
# we have to put the extra certs in with a prefix, and then one of our init scripts
# (02_get_kube_certs.sh) will expand the prefixed certs into the equivalent directory
# so that they'll be installed correctly on startup by the certs_install script
certs_dir = os.path.join(self.config_volume, EXTRA_CA_DIRECTORY) certs_dir = os.path.join(self.config_volume, EXTRA_CA_DIRECTORY)
if os.path.exists(certs_dir): if os.path.exists(certs_dir):
for extra_cert in os.listdir(certs_dir): for extra_cert in os.listdir(certs_dir):

View file

@ -24,7 +24,7 @@ export class KubeDeployModalComponent {
this.state = 'loadingDeployments'; this.state = 'loadingDeployments';
ApiService.scGetNumDeployments().then(resp => { ApiService.scGetNumDeployments().then(resp => {
this.deploymentsStatus = resp.items.map(dep => ({ name: dep.metadata.name, numPods: dep.status.replicas })); this.deploymentsStatus = resp.items.map(dep => ({ name: dep.metadata.name, numPods: dep.spec.replicas }));
this.state = 'readyToDeploy'; this.state = 'readyToDeploy';
}).catch(err => { }).catch(err => {
this.state = 'error'; this.state = 'error';
@ -37,7 +37,7 @@ export class KubeDeployModalComponent {
deployConfiguration(): void { deployConfiguration(): void {
this.ApiService.scDeployConfiguration().then(() => { this.ApiService.scDeployConfiguration().then(() => {
const deploymentNames: string[]= this.deploymentsStatus.map(dep => dep.name); const deploymentNames: string[] = this.deploymentsStatus.map(dep => dep.name);
this.ApiService.scCycleQEDeployments({ deploymentNames }).then(() => { this.ApiService.scCycleQEDeployments({ deploymentNames }).then(() => {
this.state = 'deployed' this.state = 'deployed'
@ -46,7 +46,6 @@ export class KubeDeployModalComponent {
this.errorMessage = `Could cycle the deployments with the new configuration. Error: ${err.toString()}`; this.errorMessage = `Could cycle the deployments with the new configuration. Error: ${err.toString()}`;
}) })
}).catch(err => { }).catch(err => {
console.log(err)
this.state = 'error'; this.state = 'error';
this.errorMessage = `Could not deploy the configuration. Error: ${err.toString()}`; this.errorMessage = `Could not deploy the configuration. Error: ${err.toString()}`;
}) })