Skip to content

Commit

Permalink
FEAT: Allow client side TLS for Docker hosts (#2852)
Browse files Browse the repository at this point in the history
* FEAT: Allow client side TLS for Docker hosts

Inlcude TLS certificate in HTTPS requests when certificate
files are locally available to Kuma for a host.

* fix: refactor to satisfy linter requirements

* fix: linter
  • Loading branch information
marekful committed Aug 4, 2023
1 parent a032e11 commit bce4835
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 4 deletions.
60 changes: 56 additions & 4 deletions server/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ const axios = require("axios");
const { R } = require("redbean-node");
const version = require("../package.json").version;
const https = require("https");
const fs = require("fs");

class DockerHost {

static CertificateBasePath = process.env.DOCKER_TLS_DIR_PATH || "data/docker-tls/";
static CertificateFileNameCA = process.env.DOCKER_TLS_FILE_NAME_CA || "ca.pem";
static CertificateFileNameCert = process.env.DOCKER_TLS_FILE_NAME_CA || "cert.pem";
static CertificateFileNameKey = process.env.DOCKER_TLS_FILE_NAME_CA || "key.pem";

/**
* Save a docker host
* @param {Object} dockerHost Docker host to save
Expand Down Expand Up @@ -60,23 +67,21 @@ class DockerHost {
* @returns {number} Total amount of containers on the host
*/
static async testDockerHost(dockerHost) {

const options = {
url: "/containers/json?all=true",
headers: {
"Accept": "*/*",
"User-Agent": "Uptime-Kuma/" + version
},
httpsAgent: new https.Agent({
maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
rejectUnauthorized: false,
}),
};

if (dockerHost.dockerType === "socket") {
options.socketPath = dockerHost.dockerDaemon;
} else if (dockerHost.dockerType === "tcp") {
options.baseURL = DockerHost.patchDockerURL(dockerHost.dockerDaemon);
}
options.httpsAgent = new https.Agent(DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL));

let res = await axios.request(options);

Expand Down Expand Up @@ -111,6 +116,53 @@ class DockerHost {
}
return url;
}

/**
* Returns HTTPS agent options with client side TLS parameters if certificate files
* for the given host are available under a predefined directory path.
*
* The base path where certificates are looked for can be set with the
* 'DOCKER_TLS_DIR_PATH' environmental variable or defaults to 'data/docker-tls/'.
*
* If a directory in this path exists with a name matching the FQDN of the docker host
* (e.g. the FQDN of 'https://example.com:2376' is 'example.com' so the directory
* 'data/docker-tls/example.com/' would be searched for certificate files),
* then 'ca.pem', 'key.pem' and 'cert.pem' files are included in the agent options.
* File names can also be overridden via 'DOCKER_TLS_FILE_NAME_(CA|KEY|CERT)'.
*
* @param {String} dockerType i.e. "tcp" or "socket"
* @param {String} url The docker host URL rewritten to https://
* @return {Object}
* */
static getHttpsAgentOptions(dockerType, url) {
let baseOptions = {
maxCachedSessions: 0,
rejectUnauthorized: true
};
let certOptions = {};

let dirName = url.replace(/^https:\/\/([^/:]+)(\/|:).*$/, "$1");
let dirPath = DockerHost.CertificateBasePath + dirName + "/";
let caPath = dirPath + DockerHost.CertificateFileNameCA;
let certPath = dirPath + DockerHost.CertificateFileNameCert;
let keyPath = dirPath + DockerHost.CertificateFileNameKey;

if (dockerType === "tcp" && fs.existsSync(caPath) && fs.existsSync(certPath) && fs.existsSync(keyPath)) {
let ca = fs.readFileSync(caPath);
let key = fs.readFileSync(keyPath);
let cert = fs.readFileSync(certPath);
certOptions = {
ca,
key,
cert
};
}

return {
...baseOptions,
...certOptions
};
}
}

module.exports = {
Expand Down
3 changes: 3 additions & 0 deletions server/model/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,9 @@ class Monitor extends BeanModel {
options.socketPath = dockerHost._dockerDaemon;
} else if (dockerHost._dockerType === "tcp") {
options.baseURL = DockerHost.patchDockerURL(dockerHost._dockerDaemon);
options.httpsAgent = CacheableDnsHttpAgent.getHttpsAgent(
DockerHost.getHttpsAgentOptions(dockerHost._dockerType, options.baseURL)
);
}

log.debug("monitor", `[${this.name}] Axios Request`);
Expand Down

0 comments on commit bce4835

Please sign in to comment.