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 SetupWizard : Form { private string _TrustedFile; public SetupWizard() { 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 bool GetMasterHostPort(out string host, out string port) { foreach (ListViewItem lvi in lvwEndpoints.Items) { if (lvi.SubItems.Count > 1) { host = lvi.SubItems[1].Text; port = lvi.SubItems[2].Text; return true; } } host = null; port = null; return false; } private void EnableFeature(string feature) { FileStream fp = null; try { fp = File.Open(Icinga2InstallDir + String.Format("\\etc\\icinga2\\features-enabled\\{0}.conf", feature), FileMode.Create); using (StreamWriter sw = new StreamWriter(fp, Encoding.ASCII)) { fp = null; sw.Write(String.Format("include \"../features-available/{0}.conf\"\n", feature)); } } finally { if (fp != null) fp.Dispose(); } } 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 ShowErrorText(string text) { if (InvokeRequired) { Invoke((MethodInvoker)delegate { ShowErrorText(text); }); return; } txtError.Text = text; tbcPages.SelectedTab = tabError; } private bool RunProcess(string filename, string arguments, out string output) { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = filename; psi.Arguments = arguments; psi.CreateNoWindow = true; psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; String result = ""; using (Process proc = Process.Start(psi)) { proc.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs args) { result += args.Data + "\r\n"; }; proc.OutputDataReceived += delegate(object sender, DataReceivedEventArgs args) { result += args.Data + "\r\n"; }; proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.WaitForExit(); output = result; if (proc.ExitCode != 0) return false; } return true; } private void VerifyCertificate(string host, string port) { SetRetrievalStatus(25); string pathPrefix = Icinga2InstallDir + "\\etc\\icinga2\\pki\\" + txtInstanceName.Text; string output; if (!File.Exists(pathPrefix + ".crt")) { if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe", "pki new-cert --cn \"" + txtInstanceName.Text + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\"", out output)) { ShowErrorText(output); return; } } SetRetrievalStatus(50); _TrustedFile = Path.GetTempFileName(); if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe", "pki save-cert --host \"" + host + "\" --port \"" + port + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\" --trustedcert \"" + _TrustedFile + "\"", out output)) { ShowErrorText(output); return; } SetRetrievalStatus(100); X509Certificate2 cert = new X509Certificate2(_TrustedFile); Invoke((MethodInvoker)delegate { ShowCertificatePrompt(cert); }); } private void ConfigureService() { SetConfigureStatus(0, "Updating configuration files..."); string output; string args = ""; if (rdoNewMaster.Checked) args += " --master"; Invoke((MethodInvoker)delegate { string master_host, master_port; GetMasterHostPort(out master_host, out master_port); args += " --master_host " + master_host + "," + master_port; foreach (ListViewItem lvi in lvwEndpoints.Items) { args += " --endpoint " + lvi.SubItems[0].Text; if (lvi.SubItems.Count > 1) args += "," + lvi.SubItems[1].Text + "," + lvi.SubItems[2].Text; } }); if (rdoListener.Checked) args += " --listen ::," + txtListenerPort.Text; if (chkAcceptConfig.Checked) args += " --accept-config"; if (chkAcceptCommands.Checked) args += " --accept-commands"; args += " --ticket " + txtTicket.Text; args += " --trustedcert " + _TrustedFile; args += " --cn " + txtInstanceName.Text; args += " --zone " + txtInstanceName.Text; if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe", "node setup" + args, out output)) { ShowErrorText(output); return; } 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.Modify, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow); ds.AddAccessRule(rule); di.SetAccessControl(ds); SetConfigureStatus(75, "Installing the Icinga 2 service..."); RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe", "--scm-uninstall", out output); if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe", "daemon --validate", out output)) { ShowErrorText(output); return; } if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe", "--scm-install daemon", out output)) { ShowErrorText(output); 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 + "\\etc\\icinga2\\pki"); Directory.CreateDirectory(installDir + "\\var\\cache\\icinga2"); Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\pki"); Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\agent\\inventory"); Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\config"); Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\log"); Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\zones"); Directory.CreateDirectory(installDir + "\\var\\log\\icinga2\\compat\\archive"); Directory.CreateDirectory(installDir + "\\var\\log\\icinga2\\crash"); Directory.CreateDirectory(installDir + "\\var\\run\\icinga2\\cmd"); Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\perfdata"); Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\tmp"); } private void btnBack_Click(object sender, EventArgs e) { if (tbcPages.SelectedTab == tabError) { tbcPages.SelectedIndex = 0; return; } 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 (txtTicket.Text.Length == 0) { Warning("Please enter an agent ticket."); return; } if (rdoNoMaster.Checked) { if (lvwEndpoints.Items.Count == 0) { Warning("You need to add at least one master endpoint."); return; } string host, port; if (!GetMasterHostPort(out host, out port)) { Warning("Please enter a remote host and port for at least one of your endpoints."); return; } } if (rdoListener.Checked && (txtListenerPort.Text == "")) { Warning("You need to specify a listener port."); return; } } if (tbcPages.SelectedTab == tabFinish || tbcPages.SelectedTab == tabError) 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 || tbcPages.SelectedTab == tabError); 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]; string master_host, master_port; GetMasterHostPort(out master_host, out master_port); Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(master_host, master_port); }); 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; lvwX509Fields.Items.Clear(); 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.txtInstanceName.Text; if (eib.chkConnect.Checked) { lvi.SubItems.Add(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]); } } } }