4 using System.Collections.Generic;
5 using System.ComponentModel;
6 using System.Windows.Forms;
7 using System.Runtime.InteropServices;
8 using System.Security.Cryptography.X509Certificates;
9 using System.Threading;
10 using System.Net.NetworkInformation;
11 using System.IO.Compression;
12 using System.Diagnostics;
13 using System.ServiceProcess;
14 using System.Security.AccessControl;
18 public partial class SetupWizard : Form
20 private string _TrustedFile;
21 private string Icinga2User;
25 InitializeComponent();
27 txtInstanceName.Text = Icinga2InstanceName;
29 Icinga2User = Program.Icinga2User;
30 txtUser.Text = Icinga2User;
33 private void Warning(string message)
35 MessageBox.Show(this, message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
38 private string Icinga2InstanceName
42 IPGlobalProperties props = IPGlobalProperties.GetIPGlobalProperties();
44 string fqdn = props.HostName;
46 if (props.DomainName != "")
47 fqdn += "." + props.DomainName;
53 private bool GetMasterHostPort(out string host, out string port)
55 foreach (ListViewItem lvi in lvwEndpoints.Items) {
56 if (lvi.SubItems.Count > 1) {
57 host = lvi.SubItems[1].Text.Trim();
58 port = lvi.SubItems[2].Text.Trim();
68 private void EnableFeature(string feature)
72 fp = File.Open(Program.Icinga2DataDir + String.Format("\\etc\\icinga2\\features-enabled\\{0}.conf", feature), FileMode.Create);
73 using (StreamWriter sw = new StreamWriter(fp, Encoding.ASCII)) {
75 sw.Write(String.Format("include \"../features-available/{0}.conf\"\n", feature));
83 private void SetRetrievalStatus(int pct)
86 Invoke((MethodInvoker)delegate { SetRetrievalStatus(pct); });
90 prgRetrieveCertificate.Value = pct;
93 private void SetConfigureStatus(int pct, string message)
96 Invoke((MethodInvoker)delegate { SetConfigureStatus(pct, message); });
100 prgConfig.Value = pct;
101 lblConfigStatus.Text = message;
104 private void ShowErrorText(string text)
106 if (InvokeRequired) {
107 Invoke((MethodInvoker)delegate { ShowErrorText(text); });
111 txtError.Text = text;
112 tbcPages.SelectedTab = tabError;
115 private bool RunProcess(string filename, string arguments, out string output)
117 ProcessStartInfo psi = new ProcessStartInfo();
118 psi.FileName = filename;
119 psi.Arguments = arguments;
120 psi.CreateNoWindow = true;
121 psi.UseShellExecute = false;
122 psi.RedirectStandardOutput = true;
123 psi.RedirectStandardError = true;
127 using (Process proc = Process.Start(psi)) {
128 proc.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs args)
130 result += args.Data + "\r\n";
132 proc.OutputDataReceived += delegate (object sender, DataReceivedEventArgs args)
134 result += args.Data + "\r\n";
136 proc.BeginOutputReadLine();
137 proc.BeginErrorReadLine();
142 if (proc.ExitCode != 0)
149 private void VerifyCertificate(string host, string port)
151 SetRetrievalStatus(25);
153 string pathPrefix = Program.Icinga2DataDir + "\\etc\\icinga2\\pki\\" + txtInstanceName.Text;
154 string processArguments = "pki new-cert --cn \"" + txtInstanceName.Text + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\"";
157 if (!File.Exists(pathPrefix + ".crt")) {
158 if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
161 ShowErrorText("Running command 'icinga2.exe " + processArguments + "' produced the following output:\n" + output);
166 SetRetrievalStatus(50);
168 _TrustedFile = Path.GetTempFileName();
170 processArguments = "pki save-cert --host \"" + host + "\" --port \"" + port + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\" --trustedcert \"" + _TrustedFile + "\"";
171 if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
174 ShowErrorText("Running command 'icinga2.exe " + processArguments + "' produced the following output:\n" + output);
178 SetRetrievalStatus(100);
180 X509Certificate2 cert = new X509Certificate2(_TrustedFile);
181 Invoke((MethodInvoker)delegate { ShowCertificatePrompt(cert); });
182 } catch (Exception e) {
183 ShowErrorText("Failed to receive certificate: " + e.Message);
187 private void ConfigureService()
189 SetConfigureStatus(0, "Updating configuration files...");
195 Invoke((MethodInvoker)delegate
197 string master_host, master_port;
198 GetMasterHostPort(out master_host, out master_port);
200 args += " --master_host " + master_host + "," + master_port;
202 foreach (ListViewItem lvi in lvwEndpoints.Items) {
203 args += " --endpoint " + lvi.SubItems[0].Text.Trim();
205 if (lvi.SubItems.Count > 1)
206 args += "," + lvi.SubItems[1].Text.Trim() + "," + lvi.SubItems[2].Text.Trim();
210 if (rdoListener.Checked)
211 args += " --listen ::," + txtListenerPort.Text.Trim();
213 if (chkAcceptConfig.Checked)
214 args += " --accept-config";
216 if (chkAcceptCommands.Checked)
217 args += " --accept-commands";
219 string ticket = txtTicket.Text.Trim();
221 if (ticket.Length > 0)
222 args += " --ticket \"" + ticket + "\"";
224 args += " --trustedcert \"" + _TrustedFile + "\"";
225 args += " --cn \"" + txtInstanceName.Text.Trim() + "\"";
226 args += " --zone \"" + txtInstanceName.Text.Trim() + "\"";
228 foreach (ListViewItem lvi in lvwGlobalZones.Items) {
229 args += " --global_zones " + lvi.SubItems[0].Text.Trim();
232 if (chkDisableConf.Checked)
233 args += " --disable-confd";
235 if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
238 ShowErrorText("Running command 'icinga2.exe " + "node setup" + args + "' produced the following output:\n" + output);
242 SetConfigureStatus(50, "Setting ACLs for the Icinga 2 directory...");
244 string serviceUser = txtUser.Text.Trim();
246 DirectoryInfo di = new DirectoryInfo(Program.Icinga2InstallDir);
247 DirectorySecurity ds = di.GetAccessControl();
248 FileSystemAccessRule rule = new FileSystemAccessRule(serviceUser,
249 FileSystemRights.Modify,
250 InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
252 ds.AddAccessRule(rule);
253 di.SetAccessControl(ds);
254 } catch (System.Security.Principal.IdentityNotMappedException) {
255 ShowErrorText("Could not set ACLs for user \"" + serviceUser + "\". Identitiy is not mapped.\n");
259 SetConfigureStatus(75, "Installing the Icinga 2 service...");
261 RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
265 if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
268 ShowErrorText("Running command 'icinga2.exe daemon --validate' produced the following output:\n" + output);
272 if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
273 "--scm-install --scm-user \"" + serviceUser + "\" daemon",
275 ShowErrorText("\nRunning command 'icinga2.exe --scm-install --scm-user \"" +
276 serviceUser + "\" daemon' produced the following output:\n" + output);
280 if (chkInstallNSCP.Checked) {
281 SetConfigureStatus(85, "Waiting for NSClient++ installation to complete...");
283 Process proc = new Process();
284 proc.StartInfo.FileName = "msiexec.exe";
285 proc.StartInfo.Arguments = "/i \"" + Program.Icinga2InstallDir + "\\sbin\\NSCP.msi\"";
290 SetConfigureStatus(100, "Finished.");
292 // Override the completed text
293 lblSetupCompleted.Text = "The Icinga 2 Windows client was set up successfully.";
295 // Add a note for the user for ticket-less signing
296 if (ticket.Length == 0) {
297 lblSetupCompleted.Text += "\n\nTicket was not specified. Please sign the certificate request on the Icinga 2 master node (requires v2.8+).";
303 private void FinishConfigure()
305 if (InvokeRequired) {
306 Invoke((MethodInvoker)FinishConfigure);
310 tbcPages.SelectedTab = tabFinish;
313 private void btnBack_Click(object sender, EventArgs e)
315 if (tbcPages.SelectedTab == tabError) {
316 tbcPages.SelectedIndex = 0;
322 if (tbcPages.SelectedTab == tabVerifyCertificate)
325 tbcPages.SelectedIndex -= offset;
328 private void btnNext_Click(object sender, EventArgs e)
330 if (tbcPages.SelectedTab == tabParameters) {
331 if (txtInstanceName.Text.Length == 0) {
332 Warning("Please enter an instance name.");
336 if (lvwEndpoints.Items.Count == 0) {
337 Warning("You need to add at least one master/satellite endpoint.");
342 if (!GetMasterHostPort(out host, out port)) {
343 Warning("Please enter a remote host and port for at least one of your endpoints.");
347 if (rdoListener.Checked && (txtListenerPort.Text == "")) {
348 Warning("You need to specify a listener port.");
352 if (txtUser.Text.Length == 0) {
353 Warning("Icinga 2 service user may not be empty.");
358 if (tbcPages.SelectedTab == tabFinish || tbcPages.SelectedTab == tabError)
361 tbcPages.SelectedIndex++;
364 private void btnCancel_Click(object sender, EventArgs e)
369 private void tbcPages_SelectedIndexChanged(object sender, EventArgs e)
373 btnBack.Enabled = (tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabError);
374 btnNext.Enabled = (tbcPages.SelectedTab == tabParameters || tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabFinish);
376 if (tbcPages.SelectedTab == tabFinish) {
377 btnNext.Text = "&Finish >";
378 btnCancel.Enabled = false;
381 if (tbcPages.SelectedTab == tabRetrieveCertificate) {
382 ListViewItem lvi = lvwEndpoints.Items[0];
384 string master_host, master_port;
385 GetMasterHostPort(out master_host, out master_port);
387 Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(master_host, master_port); });
391 if (tbcPages.SelectedTab == tabConfigure) {
392 Thread thread = new Thread(ConfigureService);
397 private void RadioListener_CheckedChanged(object sender, EventArgs e)
399 txtListenerPort.Enabled = rdoListener.Checked;
402 private void AddCertificateField(string name, string shortValue, string longValue = null)
404 ListViewItem lvi = new ListViewItem();
406 lvi.SubItems.Add(shortValue);
407 if (longValue == null)
408 longValue = shortValue;
410 lvwX509Fields.Items.Add(lvi);
413 private string PadText(string input)
417 for (int i = 0; i < input.Length; i += 2) {
422 if (input.Length - i < 2)
423 len = input.Length - i;
424 output += input.Substring(i, len);
430 private void ShowCertificatePrompt(X509Certificate2 certificate)
432 txtX509Issuer.Text = certificate.Issuer;
433 txtX509Subject.Text = certificate.Subject;
435 lvwX509Fields.Items.Clear();
437 AddCertificateField("Version", "V" + certificate.Version.ToString());
438 AddCertificateField("Serial number", certificate.SerialNumber);
439 AddCertificateField("Signature algorithm", certificate.SignatureAlgorithm.FriendlyName);
440 AddCertificateField("Valid from", certificate.NotBefore.ToString());
441 AddCertificateField("Valid to", certificate.NotAfter.ToString());
443 string pkey = BitConverter.ToString(certificate.PublicKey.EncodedKeyValue.RawData).Replace("-", " ");
444 AddCertificateField("Public key", certificate.PublicKey.Oid.FriendlyName + " (" + certificate.PublicKey.Key.KeySize + " bits)", pkey);
446 string thumbprint = PadText(certificate.Thumbprint);
447 AddCertificateField("Thumbprint", thumbprint);
449 tbcPages.SelectedTab = tabVerifyCertificate;
452 private void btnAddEndpoint_Click(object sender, EventArgs e)
454 EndpointInputBox eib = new EndpointInputBox();
456 if (eib.ShowDialog(this) == DialogResult.Cancel)
459 ListViewItem lvi = new ListViewItem();
460 lvi.Text = eib.txtInstanceName.Text;
462 if (eib.chkConnect.Checked) {
463 lvi.SubItems.Add(eib.txtHost.Text);
464 lvi.SubItems.Add(eib.txtPort.Text);
467 lvwEndpoints.Items.Add(lvi);
470 private void lvwEndpoints_SelectedIndexChanged(object sender, EventArgs e)
472 btnRemoveEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
473 btnEditEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
476 private void lvwX509Fields_SelectedIndexChanged(object sender, EventArgs e)
478 if (lvwX509Fields.SelectedItems.Count == 0)
481 ListViewItem lvi = lvwX509Fields.SelectedItems[0];
483 txtX509Field.Text = Convert.ToString(lvi.Tag);
486 private void btnRemoveEndpoint_Click(object sender, EventArgs e)
488 while (lvwEndpoints.SelectedItems.Count > 0) {
489 lvwEndpoints.Items.Remove(lvwEndpoints.SelectedItems[0]);
493 private void chkRunServiceAsThisUser_CheckedChanged(object sender, EventArgs e)
495 txtUser.Enabled = !txtUser.Enabled;
496 if (!txtUser.Enabled)
497 txtUser.Text = Icinga2User;
500 private void btnEditEndpoint_Click(object sender, EventArgs e)
502 ListViewItem lvi = lvwEndpoints.SelectedItems[0];
503 EndpointInputBox eib = new EndpointInputBox();
505 eib.Text = "Edit Endpoint";
506 eib.txtInstanceName.Text = lvi.SubItems[0].Text;
508 if (lvi.SubItems.Count >= 2) {
509 eib.txtHost.Text = lvi.SubItems[1].Text;
510 eib.txtPort.Text = lvi.SubItems[2].Text;
511 eib.chkConnect.Checked = true;
514 if (eib.ShowDialog(this) == DialogResult.Cancel)
517 lvwEndpoints.Items.Remove(lvi);
519 ListViewItem lvi2 = new ListViewItem();
520 lvi2.Text = eib.txtInstanceName.Text;
522 if (eib.chkConnect.Checked) {
523 lvi2.SubItems.Add(eib.txtHost.Text);
524 lvi2.SubItems.Add(eib.txtPort.Text);
527 lvwEndpoints.Items.Add(lvi2);
530 private void btnAddGlobalZone_Click(object sender, EventArgs e)
532 GlobalZonesInputBox gzib = new GlobalZonesInputBox(lvwGlobalZones.Items);
534 if (gzib.ShowDialog(this) == DialogResult.Cancel)
537 ListViewItem lvi = new ListViewItem();
538 lvi.Text = gzib.txtGlobalZoneName.Text;
540 lvwGlobalZones.Items.Add(lvi);
543 private void btnRemoveGlobalZone_Click(object sender, EventArgs e)
545 while (lvwGlobalZones.SelectedItems.Count > 0) {
546 lvwGlobalZones.Items.Remove(lvwGlobalZones.SelectedItems[0]);
550 private void lvwGlobalZones_SelectedIndexChanged(object sender, EventArgs e)
552 btnEditGlobalZone.Enabled = lvwGlobalZones.SelectedItems.Count > 0;
553 btnRemoveGlobalZone.Enabled = lvwGlobalZones.SelectedItems.Count > 0;
556 private void btnEditGlobalZone_Click(object sender, EventArgs e)
558 ListViewItem lvi = lvwGlobalZones.SelectedItems[0];
559 GlobalZonesInputBox gzib = new GlobalZonesInputBox(lvwGlobalZones.Items);
561 gzib.Text = "Edit Global Zone";
562 gzib.txtGlobalZoneName.Text = lvi.SubItems[0].Text;
564 if (gzib.ShowDialog(this) == DialogResult.Cancel)
567 lvwGlobalZones.Items.Remove(lvi);
569 ListViewItem lvi2 = new ListViewItem();
570 lvi2.Text = gzib.txtGlobalZoneName.Text;
572 lvwGlobalZones.Items.Add(lvi2);
575 private void checkBox1_CheckedChanged(object sender, EventArgs e)
580 private void SetupWizard_Load(object sender, EventArgs e)
582 this.MinimumSize = this.Size;
583 this.MaximumSize = this.Size;