security/acme-client: add support for Google Cloud DNS API, closes #549

This commit is contained in:
Frank Wall
2019-07-08 01:13:09 +02:00
parent 433d0b4e5d
commit a8b8856230
5 changed files with 117 additions and 0 deletions
@@ -372,4 +372,22 @@ class SettingsController extends ApiMutableModelControllerBase
return $result;
}
/**
* Check wether the Google Cloud plugin is installed.
* @return array status action
*/
public function getGcloudPluginStatusAction()
{
$result = array("result" => "0");
$mdlAcme = $this->getModel();
// Check if the required plugin is installed
if ((string)$mdlAcme->isPluginInstalled('google-cloud-sdk') == "1") {
$result['result'] = "1";
}
return $result;
}
}
@@ -401,6 +401,30 @@
<label>Key</label>
<type>text</type>
</field>
<field>
<label>Google Cloud DNS</label>
<type>header</type>
<style>table_dns table_dns_gcloud</style>
</field>
<field>
<label><![CDATA[NOTE: First you must create a <a target="_blank" href="https://cloud.google.com/iam/docs/creating-managing-service-account-keys">service account key</a> using the GCP Console and enable the <a target="_blank" href="https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview">Cloud Resource Manager API</a>. Afterwards paste the full JSON key in the textbox below.]]></label>
<type>info</type>
</field>
<field>
<id>validation.dns_gcloud_key</id>
<label>JSON Key</label>
<type>textbox</type>
<help>Provide a service account key in JSON format for your Google Cloud account.</help>
</field>
<field>
<label>Action required</label>
<type>header</type>
<style>table_dns table_dns_gcloud gcloud_plugin_warning</style>
</field>
<field>
<label><![CDATA[Please manually install the plugin "os-google-cloud-sdk" to enable support for Google Cloud DNS.]]></label>
<type>info</type>
</field>
<field>
<label>GoDaddy</label>
<type>header</type>
@@ -363,6 +363,7 @@
<dns_freedns>FreeDNS API</dns_freedns>
<dns_gandi_livedns>Gandi LiveDNS API</dns_gandi_livedns>
<dns_gd>GoDaddy.com API</dns_gd>
<dns_gcloud>Google Cloud DNS API</dns_gcloud>
<dns_gdnsdk>GratisDNS.dk</dns_gdnsdk>
<dns_hostingde>hosting.de API</dns_hostingde>
<dns_he>Hurricane Electric</dns_he>
@@ -518,6 +519,9 @@
<dns_gandi_livedns_key type="TextField">
<Required>N</Required>
</dns_gandi_livedns_key>
<dns_gcloud_key type="TextField">
<Required>N</Required>
</dns_gcloud_key>
<dns_gd_key type="TextField">
<Required>N</Required>
</dns_gd_key>
@@ -56,6 +56,12 @@ POSSIBILITY OF SUCH DAMAGE.
if ($("#validation\\.method").val() == 'dns01') {
$("."+service_id).show();
}
// Show a warning if the Google Cloud SDK plugin is missing.
ajaxCall(url="/api/acmeclient/settings/getGcloudPluginStatus", sendData={}, callback=function(data,status) {
if (data['result'] != 0) {
$(".gcloud_plugin_warning").hide();
}
});
});
$("#validation\\.http_service").change(function(){
var service_id = 'table_http_' + $(this).val();
@@ -72,6 +78,7 @@ POSSIBILITY OF SUCH DAMAGE.
$("#validation\\.http_service").change();
});
$("#validation\\.method").change();
})
});
@@ -417,6 +417,7 @@ function run_acme_validation($certObj, $valObj, $acctObj)
// Required to run pre-defined commands.
$backend = new Backend();
$modelObj = new OPNsense\AcmeClient\AcmeClient;
// Collect account information
$account_conf_dir = "/var/etc/acme-client/accounts/" . $acctObj->id;
@@ -567,6 +568,7 @@ function run_acme_validation($certObj, $valObj, $acctObj)
// Prepare DNS-01 hooks
if ($val_method == 'dns01') {
// Some common stuff
$val_id = preg_replace("/[^a-zA-Z0-9]/", "", (string)$valObj->id);
$secret_key_filename = "${configdir}/secret.key";
$acme_args[] = '--dnssleep ' . $valObj->dns_sleep;
@@ -655,6 +657,42 @@ function run_acme_validation($certObj, $valObj, $acctObj)
case 'dns_gandi_livedns':
$proc_env['GANDI_LIVEDNS_KEY'] = (string)$valObj->dns_gandi_livedns_key;
break;
case 'dns_gcloud':
# Google Cloud SDK must be installed.
if ((string)$modelObj->isPluginInstalled('google-cloud-sdk') != "1") {
log_error("AcmeClient: Google Cloud SDK plugin is NOT installed. Please install os-google-cloud-sdk.");
return(1);
}
# We need a valid Google Cloud JSON key.
if (!empty((string)$valObj->dns_gcloud_key)) {
# Extract the gcloud project from the key data.
$_gcloud_data = json_decode((string)$valObj->dns_gcloud_key);
$gcloud_project = $_gcloud_data->project_id;
$gcloud_account = $_gcloud_data->client_email;
if (empty($gcloud_project)) {
log_error("AcmeClient: unable to extract project name from Google Cloud DNS JSON key");
return(1);
} else {
log_error("AcmeClient: Google Cloud DNS project name: ${gcloud_project}");
}
} else {
log_error("AcmeClient: no key for Google Cloud DNS was specified");
return(1);
}
# Preparations for gcloud CLI.
$gcloud_config = "acme-${val_id}";
$gcloud_key_file = "/tmp/acme_" . (string)$valObj->dns_service . "_${val_id}.json";
file_put_contents($gcloud_key_file, (string)$valObj->dns_gcloud_key);
chmod($gcloud_key_file, 0600);
$proc_env['CLOUDSDK_ACTIVE_CONFIG_NAME'] = $gcloud_config;
$proc_env['CLOUDSDK_CORE_PROJECT'] = $gcloud_project;
# Ensure that a working gcloud config exists.
run_shell_command("/usr/local/bin/gcloud config configurations create ${gcloud_config}",$proc_env);
run_shell_command("/usr/local/bin/gcloud config configurations activate ${gcloud_config}",$proc_env);
run_shell_command("/usr/local/bin/gcloud auth activate-service-account --key-file=${gcloud_key_file}",$proc_env);
run_shell_command("/usr/local/bin/gcloud config set account ${gcloud_account}",$proc_env);
run_shell_command("/usr/local/bin/gcloud config set project ${gcloud_project}",$proc_env);
break;
case 'dns_gd':
$proc_env['GD_Key'] = (string)$valObj->dns_gd_key;
$proc_env['GD_Secret'] = (string)$valObj->dns_gd_secret;
@@ -1244,6 +1282,32 @@ function dump_postponed_updates()
}
}
function run_shell_command($proc_cmd, $proc_env = array())
{
$proc_desc = array( // descriptor array for proc_open()
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$proc_pipes = array();
$proc = proc_open($proc_cmd, $proc_desc, $proc_pipes, null, $proc_env);
// Make sure the resource could be setup properly
if (is_resource($proc)) {
// Close all pipes
fclose($proc_pipes[0]);
fclose($proc_pipes[1]);
fclose($proc_pipes[2]);
// Get exit code
$result = proc_close($proc);
log_error(sprintf("AcmeClient: The shell command '%s' returned exit code '%d'", $proc_cmd, $result));
return($result);
} else {
log_error(sprintf("AcmeClient: Unable to prepare shell command '%s'",$proc_cmd));
return(1);
}
}
// taken from certs.inc
function local_cert_get_subject_array($str_crt, $decode = true)
{