Managing TLS Certificates
Relevant source files
The following files were used as context for generating this wiki page:
In huskyCI, Transport Layer Security (TLS) is used to secure communication between internal services, most notably between the huskyCI_API service and the huskyCI_Docker_API. This ensures that requests to the Docker daemon are authenticated and encrypted, preventing unauthorized access. The project provides two primary mechanisms for managing these certificates: build-time generation via shell scripts for local development, and runtime provisioning from environment variables for containerized deployments.
This document outlines the architecture and processes for generating, provisioning, and using these TLS certificates within the huskyCI ecosystem.
Build-Time Certificate Generation
For local development environments set up via docker-compose, huskyCI uses a set of shell scripts to generate all necessary certificates and keys. This process is orchestrated by run-create-certs.sh, which in turn calls create-certs.sh with different parameters.
Sources: deployments/scripts/run-create-certs.sh, deployments/scripts/create-certs.sh
Execution Flow
The run-create-certs.sh script automates the creation of a Certificate Authority (CA), server certificates for dockerapi and nginx, a client certificate for huskyapi, and a separate TLS certificate for the API.
The following diagram illustrates the sequence of commands executed by the script:
Certificate Generation Script (create-certs.sh)
The core logic for certificate creation resides in create-certs.sh, which is a wrapper around openssl commands. It can generate different types of certificates based on the -m (mode) flag.
Script Arguments
The script accepts several arguments to customize the certificate generation process.
| Argument | Alias | Description | Default Value |
|---|---|---|---|
--mode | -m | The type of certificate to create: ca, server, client, or tls. | N/A (Required) |
--hostname | -h | The DNS hostname for a server certificate or the name of a client. | N/A (Required for non-ca modes) |
--hostip | -hip | The IP address for the server certificate's Subject Alternative Name (SAN). | None |
--password | -pw | The password for CA key generation. | N/A (Required) |
--targetdir | -t | The target directory for the output certificate and key files. | N/A (Required) |
--expirationdays | -e | The certificate's validity period in days. | 700 |
--ca-subj | The subject string for the CA certificate. | /C=GB/ST=London/L=London/O=ExampleCompany/OU=IT/CN=example.com/emailAddress=test@example.com |
Sources: deployments/scripts/create-certs.sh:16-78
Certificate Types
- CA Certificate (
-m ca): ThecreateCAfunction generates a new root Certificate Authority key (ca-key.pem) and certificate (ca.pem). The key is encrypted with the provided password.
Sources: deployments/scripts/create-certs.sh:80-85 - Server Certificate (
-m server): ThecreateServerCertfunction generates a server key and a certificate signed by the CA. It includes asubjectAltNameextension to support both DNS hostname and IP address validation.
Sources: deployments/scripts/create-certs.sh:96-112 - Client Certificate (
-m client): ThecreateClientCertfunction generates a client key and a certificate signed by the CA. It includes theextendedKeyUsage = clientAuthextension, which is necessary for client authentication (mTLS).
Sources: deployments/scripts/create-certs.sh:114-125 - API TLS Certificate (
-m tls): ThecreateTLSCertfunction generates a self-signed certificate for the API, with a hardcoded Common Name oflocalhost.
Sources: deployments/scripts/create-certs.sh:90-93
Runtime Certificate Provisioning
For environments where build-time scripts are not practical (e.g., Kubernetes or other container orchestration platforms), the huskyCI_API service can create the necessary certificate files at startup from environment variables.
This process is triggered during the API's initial health check sequence.
Sources: api/util/api/api.go
API Startup Sequence
When the API service starts, it runs CheckHuskyRequirements, which in turn calls checkDockerHosts. If the infrastructure is set to docker, this function calls createAPIKeys, which reads certificate data from environment variables and writes them to the filesystem.
This diagram shows the runtime provisioning flow:
Environment Variables and Created Files
The createAPIKeys function and its helpers look for specific environment variables. If found, their contents are written to corresponding files within the API container.
| Environment Variable | Created File | Function |
|---|---|---|
HUSKYCI_DOCKERAPI_CERT_FILE_VALUE | /home/application/current/api/cert.pem | createAPICert |
HUSKYCI_DOCKERAPI_CERT_KEY_VALUE | /home/application/current/api/key.pem | createAPIKey |
HUSKYCI_DOCKERAPI_API_TLS_CERT_VALUE | /home/application/current/api/api-tls-cert.pem | createAPITLSCert |
HUSKYCI_DOCKERAPI_API_TLS_KEY_VALUE | /home/application/current/api/api-tls-key.pem | createAPITLSKey |
HUSKYCI_DOCKERAPI_CERT_CA_VALUE | /home/application/current/api/ca.pem | createAPICA |
Sources: api/util/api/api.go:261-346
Certificate Usage in Docker
The docker-compose.yml file defines how the generated certificates are used to establish a secure TLS connection between the api and dockerapi services.
Sources: deployments/docker-compose.yml
Service Configuration
The diagram below shows how the services are configured to use the certificates for mutual TLS (mTLS).
Docker API Service (dockerapi)
The dockerapi service runs a Docker-in-Docker (dind) container. Its command is configured to expose the Docker daemon over a TLS-encrypted TCP socket on port 2376.
# deployments/docker-compose.yml:8-10
services:
dockerapi:
command: "dockerd --host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:2376"
The service mounts the generated certificates from ../deployments/certs into its /data/certs directory, which the Docker daemon is configured to use.
Sources: deployments/docker-compose.yml:8-15
huskyCI API Service (api)
The api service needs to authenticate itself to the dockerapi service. It does this by mounting the client certificate, key, and the CA certificate.
# deployments/docker-compose.yml:38-40
volumes:
- ../deployments/certs/server-dockerapi-cert.pem:/go/src/github.com/globocom/huskyCI/cert.pem:ro
- ../deployments/certs/server-dockerapi-key.pem:/go/src/github.com/globocom/huskyCI/key.pem:ro
- ../deployments/certs/ca.pem:/go/src/github.com/globocom/huskyCI/ca.pem:ro
The application code is made aware of the certificate location via an environment variable:
# deployments/docker-compose.yml:33
HUSKYCI_DOCKERAPI_CERT_PATH: /go/src/github.com/globocom/huskyCI/
The Go application then uses these files to create a secure HTTP client for all communications with the Docker API, as seen when dockerRun is called to execute a security test.
Sources: deployments/docker-compose.yml:21-41, api/securitytest/securitytest.go:107-115
Summary
huskyCI employs a comprehensive strategy for TLS certificate management to secure inter-service communication. For local development, it provides a convenient, script-based method to generate a full PKI setup. For more flexible or production-like deployments, it supports provisioning certificates at runtime via environment variables. This dual approach ensures that the critical communication path between the API and the Docker engine is always authenticated and encrypted, forming a foundational security feature of the huskyCI architecture.