Skip to content

Instantly share code, notes, and snippets.

@ankurcha
Created August 19, 2016 17:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ankurcha/895b06d5f02094983e083c742470cab4 to your computer and use it in GitHub Desktop.
Save ankurcha/895b06d5f02094983e083c742470cab4 to your computer and use it in GitHub Desktop.
Kubernetes secrets creation scripts for mutual authentication and ssl generation

Generating self-signed root CA certificate and private key

To simplify this process, we will use CFSSL - CloudFlare's PKI/TLS toolkit

Install CFSSL

brew install cfssl

Create CA certificate (and csr)

The ./regen-ca-certs.sh script will create the the self-signed ca certificate for each environment and application. If a csr and/or .pem already exists, it will NOT be overwritten.

./regen-ca-certs.sh <app_name>

Here <app_name> is the name of the application for which the ssl certs need to be generated.

Generating a local-issued certificates and private key.

./regen-ssl-certs.sh

This scripts generates the server and client certificates for each environment. The csr for each certificate is stored in the config/ directory under each environment. The certificates are created with a 8760h (1 year) expiration and should be refreshed before expiry.

Usage in gRPC services

Mutual authentication (or "client-side authentication") configuration is similar to the server by providing truststores, a client certificate and private key to the client channel. The server must also be configured to request a certificate from clients, as well as truststores for which client certificates it should allow.

Server setup

SslContext sslContext = GrpcSslContexts
        .forServer(certChainFile, privateKeyFile) // path to the environment specific server certificate and corresponding key
        .trustManager(caCertFile)                 // path to the environment specific CA certificate
        .build();

NettyServerBuilder.forPort(8443)
    .addService(new ServiceImpl())
    .sslContext(sslContext)
    .build();

Client setup

SslContext sslContext = GrpcSslContexts.forClient()
        .trustManager(caCertFile)                   // path to the environment specific CA certificate
        .keyManager(clientCertFile, clientKeyFile)  // path to the environment specific client certificate and corresponding key
        .build();

NettyChannelBuilder.forAddress(serverHost, serverPort)
    .negotiationType(NegotiationType.TLS)
    .sslContext(sslContext)
    .build();

Negotiated client certificates are available in the SSLSession, which is found in the SSL_SESSION_KEY attribute of ServerCall. A server interceptor can provide details in the current Context.

public final static Context.Key<SSLSession> SSL_SESSION_CONTEXT = Context.key("SSLSession");

@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(MethodDescriptor<ReqT, RespT> method,
    ServerCall<RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
    SSLSession sslSession = call.attributes().get(ServerCall.SSL_SESSION_KEY);
    if (sslSession == null) {
        return next.startCall(method, call, headers)
    }
    return Contexts.interceptCall(
        Context.current().withValue(SSL_SESSION_CONTEXT, clientContext),
        method, call, headers, next);
}
#!/bin/bash -e
# create directories
for env in qa staging prod
do
mkdir -p ${env}/ca
done
for env in qa staging prod
do
pushd ${env}/ca
if [ -e csr_ca.json ]
then
echo "csr_ca.json already exists for ${env}"
else
echo "Writing csr_ca.json for ${env}"
echo '
{
"CN": "Reporting and Analytics CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C" : "US",
"L" : "Seattle",
"O" : "Brightcove Inc.",
"OU": "Reporting and Analytics",
"ST": "Washington"
}
]
}
' > csr_ca.json
fi
if [ -e ca-key.pem ]
then
echo "ca-key.pem already exists for ${env} skipping ca cert generation"
else
echo "Generating CA for ${env}"
cfssl genkey -initca csr_ca.json | cfssljson -bare ca
fi
echo "Creating secrets.yaml for kubernetes"
cat > secrets.yaml <<EOF
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: ca-pki
data:
ca-crt: $(cat ca.pem | base64)
EOF
echo "Secrets for env ${env} can be uploaded to kubernetes using:"
echo "kubectl create -f ${env}/ca/secrets.yaml --namespace=${env}"
popd
done
#!/bin/bash -e
app_name=$1
if [[ -n "${app_name}" ]]; then
echo "Creating ssl certificates for ${app_name}"
else
echo "app_name argument missing"
exit -1
fi
if [ $app_name = "ca" ]; then
echo "app_name cannot be 'ca' this is reserved for the CA location"
exit -127
fi
# create directories
for env in qa staging prod
do
pushd $env > /dev/null
CA_DIR=$(pwd)/ca
mkdir -p ${app_name}/{server,client}
pushd ${app_name} > /dev/null
# create server ssl cert
pushd server > /dev/null
echo '{"signing": {"default": {"expiry": "8760h","usages": ["signing","key encipherment","server auth"]}}}' > server.json
cat > server-csr.json <<EOF
{
"CN": "${app_name}.${env}.rnatest.brightcove.com",
"hosts": ["127.0.0.1", "${app_name}.${env}.rnatest.brightcove.com"],
"key": {"algo": "rsa","size": 2048},
"names": [{"C" : "US", "L" : "Seattle", "O" : "Brightcove Inc.", "OU": "Reporting and Analytics", "ST": "Washington"}]
}
EOF
cfssl gencert -ca=$CA_DIR/ca.pem -ca-key=$CA_DIR/ca-key.pem -config=server.json server-csr.json | cfssljson -bare server
popd
# create client ssl cert
pushd client > /dev/null
echo '{"signing": {"default": {"expiry": "8760h","usages": ["signing","key encipherment","client auth"]}}}' > client.json
cat > client-csr.json <<EOF
{
"CN": "client",
"hosts": [""],
"key": {"algo": "rsa","size": 2048},
"names": [{"C" : "US", "L" : "Seattle", "O" : "Brightcove Inc.", "OU": "Reporting and Analytics", "ST": "Washington"}]
}
EOF
cfssl gencert -ca=$CA_DIR/ca.pem -ca-key=$CA_DIR/ca-key.pem -config=client.json client-csr.json | cfssljson -bare client
popd
echo "Creating secrets.yaml for kubernetes"
cat > secrets.yaml <<EOF
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: ${app_name}.server-pki
data:
server-key: $(cat server/server-key.pem | base64)
server-crt: $(cat server/server.pem | base64)
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: ${app_name}.client-pki
data:
client-key: $(cat client/client-key.pem | base64)
client-crt: $(cat client/client.pem | base64)
EOF
echo "Secrets for ${app_name} in ${env} can be uploaded to kubernetes using:"
echo "kubectl create -f ${env}/${app_name}/secrets.yaml --namespace=${env}"
# delete config, cert and key files
rm -rf server client
popd # end app_name
popd # end env
done # end loop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment