icinga2/agent/windows-setup-agent/AgentWizard.cs

435 lines
12 KiB
C#

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Net.NetworkInformation;
using Microsoft.Win32;
using System.IO.Compression;
using System.Diagnostics;
using System.ServiceProcess;
using System.Security.AccessControl;
namespace Icinga
{
public partial class AgentWizard : Form
{
public AgentWizard()
{
InitializeComponent();
txtInstanceName.Text = Icinga2InstanceName;
}
private void FatalError(string message)
{
MessageBox.Show(this, message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
private void Warning(string message)
{
MessageBox.Show(this, message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
private string Icinga2InstallDir
{
get
{
RegistryKey rk = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Icinga Development Team\\ICINGA2");
if (rk == null)
return "";
return (string)rk.GetValue("");
}
}
private string Icinga2InstanceName
{
get
{
IPGlobalProperties props = IPGlobalProperties.GetIPGlobalProperties();
string fqdn = props.HostName;
if (props.DomainName != "")
fqdn += "." + props.DomainName;
return fqdn;
}
}
private void EnableFeature(string feature)
{
using (FileStream fp = File.Open(Icinga2InstallDir + String.Format("\\etc\\icinga2\\features-enabled\\{0}.conf", feature), FileMode.Create)) {
using (StreamWriter sw = new StreamWriter(fp, Encoding.ASCII)) {
sw.Write(String.Format("include \"../features-available/{0}.conf\"\n", feature));
}
}
}
private void SetRetrievalStatus(int pct)
{
if (InvokeRequired) {
Invoke((MethodInvoker)delegate { SetRetrievalStatus(pct); });
return;
}
prgRetrieveCertificate.Value = pct;
}
private void SetConfigureStatus(int pct, string message)
{
if (InvokeRequired) {
Invoke((MethodInvoker)delegate { SetConfigureStatus(pct, message); });
return;
}
prgConfig.Value = pct;
lblConfigStatus.Text = message;
}
private void VerifyCertificate(string host, string port)
{
SetRetrievalStatus(25);
string pathPrefix = Icinga2InstallDir + "\\etc\\icinga2\\pki\\" + txtInstanceName.Text;
ProcessStartInfo psi;
if (!File.Exists(pathPrefix + ".crt")) {
psi = new ProcessStartInfo();
psi.FileName = Icinga2InstallDir + "\\sbin\\icinga2.exe";
psi.Arguments = "pki new-cert --cn \"" + txtInstanceName.Text + "\" --keyfile \"" + pathPrefix + ".key\" --certfile \"" + pathPrefix + ".crt\"";
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
using (Process proc = Process.Start(psi)) {
proc.WaitForExit();
if (proc.ExitCode != 0) {
Invoke((MethodInvoker)delegate { FatalError("The Windows service could not be installed."); });
return;
}
}
}
SetRetrievalStatus(50);
string trustedfile = Path.GetTempFileName();
psi = new ProcessStartInfo();
psi.FileName = Icinga2InstallDir + "\\sbin\\icinga2.exe";
psi.Arguments = "pki save-cert --host \"" + host + "\" --port \"" + port + "\" --keyfile \"" + pathPrefix + ".key\" --certfile \"" + pathPrefix + ".crt\" --trustedfile \"" + trustedfile + "\"";
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
using (Process proc = Process.Start(psi)) {
proc.WaitForExit();
if (proc.ExitCode != 0) {
Invoke((MethodInvoker)delegate { FatalError("Could not retrieve the master's X509 certificate."); });
return;
}
}
SetRetrievalStatus(100);
X509Certificate2 cert = new X509Certificate2(trustedfile);
Invoke((MethodInvoker)delegate { ShowCertificatePrompt(cert); });
}
private void ConfigureService()
{
SetConfigureStatus(0, "Updating configuration files...");
using (FileStream fp = File.Open(Icinga2InstallDir + "\\etc\\icinga2\\features-available\\agent.conf", FileMode.Create)) {
using (StreamWriter sw = new StreamWriter(fp, Encoding.ASCII)) {
sw.Write(
"/**\n" +
" * The agent listener accepts checks from agents.\n" +
" */\n" +
"\n" +
"library \"agent\"\n" +
"\n" +
"object AgentListener \"agent\" {\n" +
" cert_path = SysconfDir + \"/icinga2/pki/agent/agent.crt\"\n" +
" key_path = SysconfDir + \"/icinga2/pki/agent/agent.key\"\n" +
" ca_path = SysconfDir + \"/icinga2/pki/agent/ca.crt\"\n"
);
/*if (rdoNoMaster.Checked)
sw.Write(" upstream_name = \"{0}\"\n", txtMasterInstance.Text);*/
if (rdoListener.Checked)
sw.Write(" bind_port = \"{0}\"\n", txtListenerPort.Text);
/*if (rdoConnect.Checked)
sw.Write(
" upstream_host = \"{0}\"\n" +
" upstream_port = \"{1}\"\n", txtPeerHost.Text, txtPeerPort.Text
);*/
sw.Write("}\n");
}
}
EnableFeature("api");
EnableFeature("checker");
SetConfigureStatus(50, "Setting ACLs for the Icinga 2 directory...");
DirectoryInfo di = new DirectoryInfo(Icinga2InstallDir);
DirectorySecurity ds = di.GetAccessControl();
FileSystemAccessRule rule = new FileSystemAccessRule("NT AUTHORITY\\NetworkService",
FileSystemRights.ReadAndExecute | FileSystemRights.Write | FileSystemRights.ListDirectory,
InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
ds.AddAccessRule(rule);
di.SetAccessControl(ds);
SetConfigureStatus(75, "Installing the Icinga 2 service...");
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = Icinga2InstallDir + "\\sbin\\icinga2.exe";
psi.Arguments = "--scm-uninstall";
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
using (Process proc = Process.Start(psi)) {
proc.WaitForExit();
}
psi = new ProcessStartInfo();
psi.FileName = Icinga2InstallDir + "\\sbin\\icinga2.exe";
psi.Arguments = "--scm-install daemon";
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
using (Process proc = Process.Start(psi)) {
proc.WaitForExit();
if (proc.ExitCode != 0) {
Invoke((MethodInvoker)delegate { FatalError("The Windows service could not be installed."); });
return;
}
}
SetConfigureStatus(100, "Finished.");
FinishConfigure();
}
private void FinishConfigure()
{
if (InvokeRequired) {
Invoke((MethodInvoker)FinishConfigure);
return;
}
tbcPages.SelectedTab = tabFinish;
}
private void AgentWizard_Shown(object sender, EventArgs e)
{
string installDir = Icinga2InstallDir;
if (installDir == "")
FatalError("Icinga 2 does not seem to be installed properly.");
/* TODO: This is something the NSIS installer should do */
Directory.CreateDirectory(installDir + "\\var\\cache\\icinga2");
Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\agent\\inventory");
Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\cluster\\config");
Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\cluster\\log");
Directory.CreateDirectory(installDir + "\\var\\log\\icinga2\\compat\\archive");
Directory.CreateDirectory(installDir + "\\var\\run\\icinga2\\cmd");
Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\perfdata");
Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\tmp");
Directory.CreateDirectory(installDir + "\\etc\\icinga2\\pki\\agent");
}
private void btnBack_Click(object sender, EventArgs e)
{
int offset = 1;
if (tbcPages.SelectedTab == tabVerifyCertificate)
offset++;
tbcPages.SelectedIndex -= offset;
}
private void btnNext_Click(object sender, EventArgs e)
{
if (tbcPages.SelectedTab == tabParameters) {
if (txtInstanceName.Text.Length == 0) {
Warning("Please enter an instance name.");
return;
}
if (rdoNoMaster.Checked && lvwEndpoints.Items.Count == 0) {
Warning("You need to add at least one master endpoint.");
return;
}
if (rdoListener.Checked && (txtListenerPort.Text == "")) {
Warning("You need to specify a listener port.");
return;
}
}
if (tbcPages.SelectedTab == tabFinish)
Application.Exit();
tbcPages.SelectedIndex++;
}
private void btnCancel_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void tbcPages_SelectedIndexChanged(object sender, EventArgs e)
{
Refresh();
btnBack.Enabled = (tbcPages.SelectedTab == tabVerifyCertificate);
btnNext.Enabled = (tbcPages.SelectedTab == tabParameters || tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabFinish);
if (tbcPages.SelectedTab == tabFinish) {
btnNext.Text = "&Finish >";
btnCancel.Enabled = false;
}
if (tbcPages.SelectedTab == tabRetrieveCertificate) {
ListViewItem lvi = lvwEndpoints.Items[0];
Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(lvi.SubItems[0].Text, lvi.SubItems[1].Text); });
thread.Start();
}
/*if (tbcPages.SelectedTab == tabParameters &&
!File.Exists(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent\\agent.crt")) {
byte[] bytes = Convert.FromBase64String(txtBundle.Text);
MemoryStream ms = new MemoryStream(bytes);
GZipStream gz = new GZipStream(ms, CompressionMode.Decompress);
MemoryStream ms2 = new MemoryStream();
byte[] buffer = new byte[512];
int rc;
while ((rc = gz.Read(buffer, 0, buffer.Length)) > 0)
ms2.Write(buffer, 0, rc);
ms2.Position = 0;
TarReader tr = new TarReader(ms2);
tr.ReadToEnd(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent");
}*/
if (tbcPages.SelectedTab == tabConfigure) {
Thread thread = new Thread(ConfigureService);
thread.Start();
}
}
private void RadioMaster_CheckedChanged(object sender, EventArgs e)
{
lvwEndpoints.Enabled = !rdoNewMaster.Checked;
btnAddEndpoint.Enabled = !rdoNewMaster.Checked;
btnRemoveEndpoint.Enabled = !rdoNewMaster.Checked && lvwEndpoints.SelectedItems.Count > 0;
}
private void RadioListener_CheckedChanged(object sender, EventArgs e)
{
txtListenerPort.Enabled = rdoListener.Checked;
}
private void AddCertificateField(string name, string shortValue, string longValue = null)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = name;
lvi.SubItems.Add(shortValue);
if (longValue == null)
longValue = shortValue;
lvi.Tag = longValue;
lvwX509Fields.Items.Add(lvi);
}
private string PadText(string input)
{
string output = "";
for (int i = 0; i < input.Length; i += 2) {
if (output != "")
output += " ";
int len = 2;
if (input.Length - i < 2)
len = input.Length - i;
output += input.Substring(i, len);
}
return output;
}
private void ShowCertificatePrompt(X509Certificate2 certificate)
{
txtX509Issuer.Text = certificate.Issuer;
txtX509Subject.Text = certificate.Subject;
AddCertificateField("Version", "V" + certificate.Version.ToString());
AddCertificateField("Serial number", certificate.SerialNumber);
AddCertificateField("Signature algorithm", certificate.SignatureAlgorithm.FriendlyName);
AddCertificateField("Valid from", certificate.NotBefore.ToString());
AddCertificateField("Valid to", certificate.NotAfter.ToString());
string pkey = BitConverter.ToString(certificate.PublicKey.EncodedKeyValue.RawData).Replace("-", " ");
AddCertificateField("Public key", certificate.PublicKey.Oid.FriendlyName + " (" + certificate.PublicKey.Key.KeySize + " bits)", pkey);
string thumbprint = PadText(certificate.Thumbprint);
AddCertificateField("Thumbprint", thumbprint);
tbcPages.SelectedTab = tabVerifyCertificate;
}
private void btnAddEndpoint_Click(object sender, EventArgs e)
{
EndpointInputBox eib = new EndpointInputBox();
if (eib.ShowDialog(this) == DialogResult.Cancel)
return;
ListViewItem lvi = new ListViewItem();
lvi.Text = eib.txtHost.Text;
lvi.SubItems.Add(eib.txtPort.Text);
lvwEndpoints.Items.Add(lvi);
}
private void lvwEndpoints_SelectedIndexChanged(object sender, EventArgs e)
{
btnRemoveEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
}
private void lvwX509Fields_SelectedIndexChanged(object sender, EventArgs e)
{
if (lvwX509Fields.SelectedItems.Count == 0)
return;
ListViewItem lvi = lvwX509Fields.SelectedItems[0];
txtX509Field.Text = (string)lvi.Tag;
}
private void btnRemoveEndpoint_Click(object sender, EventArgs e)
{
while (lvwEndpoints.SelectedItems.Count > 0) {
lvwEndpoints.Items.Remove(lvwEndpoints.SelectedItems[0]);
}
}
}
}