]> granicus.if.org Git - icinga2/blob - agent/windows-setup-agent/SetupWizard.cs
Move NSClient++ installation step after all other wizard steps
[icinga2] / agent / windows-setup-agent / SetupWizard.cs
1 using System;
2 using System.IO;
3 using System.Text;
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 Microsoft.Win32;
12 using System.IO.Compression;
13 using System.Diagnostics;
14 using System.ServiceProcess;
15 using System.Security.AccessControl;
16
17 namespace Icinga
18 {
19         public partial class SetupWizard : Form
20         {
21                 private string _TrustedFile;
22
23                 public SetupWizard()
24                 {
25                         InitializeComponent();
26
27                         txtInstanceName.Text = Icinga2InstanceName;
28                 }
29
30                 private void FatalError(string message)
31                 {
32                         MessageBox.Show(this, message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
33                         Application.Exit();
34                 }
35
36                 private void Warning(string message)
37                 {
38                         MessageBox.Show(this, message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
39                 }
40
41                 private string Icinga2InstallDir
42                 {
43                         get
44                         {
45                                 RegistryKey rk = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Icinga Development Team\\ICINGA2");
46
47                                 if (rk == null)
48                                         return "";
49
50                                 return (string)rk.GetValue("");
51                         }
52                 }
53
54                 private string Icinga2InstanceName
55                 {
56                         get
57                         {
58                                 IPGlobalProperties props = IPGlobalProperties.GetIPGlobalProperties();
59
60                                 string fqdn = props.HostName;
61
62                                 if (props.DomainName != "")
63                                         fqdn += "." + props.DomainName;
64
65                                 return fqdn;
66                         }
67                 }
68
69                 private bool GetMasterHostPort(out string host, out string port)
70                 {
71                         foreach (ListViewItem lvi in lvwEndpoints.Items) {
72                                 if (lvi.SubItems.Count > 1) {
73                                         host = lvi.SubItems[1].Text;
74                                         port = lvi.SubItems[2].Text;
75                                         return true;
76                                 }
77                         }
78
79                         host = null;
80                         port = null;
81                         return false;
82                 }
83
84                 private void EnableFeature(string feature)
85                 {
86                         FileStream fp = null;
87                         try {
88                                 fp = File.Open(Icinga2InstallDir + String.Format("\\etc\\icinga2\\features-enabled\\{0}.conf", feature), FileMode.Create);
89                                 using (StreamWriter sw = new StreamWriter(fp, Encoding.ASCII)) {
90                                         fp = null;
91                                         sw.Write(String.Format("include \"../features-available/{0}.conf\"\n", feature));
92                                 }
93                         } finally {
94                                 if (fp != null)
95                                         fp.Dispose();
96                         }
97                 }
98
99                 private void SetRetrievalStatus(int pct)
100                 {
101                         if (InvokeRequired) {
102                                 Invoke((MethodInvoker)delegate { SetRetrievalStatus(pct); });
103                                 return;
104                         }
105
106                         prgRetrieveCertificate.Value = pct;
107                 }
108
109                 private void SetConfigureStatus(int pct, string message)
110                 {
111                         if (InvokeRequired) {
112                                 Invoke((MethodInvoker)delegate { SetConfigureStatus(pct, message); });
113                                 return;
114                         }
115
116                         prgConfig.Value = pct;
117                         lblConfigStatus.Text = message;
118                 }
119
120                 private void ShowErrorText(string text)
121                 {
122                         if (InvokeRequired) {
123                                 Invoke((MethodInvoker)delegate { ShowErrorText(text); });
124                                 return;
125                         }
126
127                         txtError.Text = text;
128                         tbcPages.SelectedTab = tabError;
129                 }
130
131                 private bool RunProcess(string filename, string arguments, out string output)
132                 {
133                         ProcessStartInfo psi = new ProcessStartInfo();
134                         psi.FileName = filename;
135                         psi.Arguments = arguments;
136                         psi.CreateNoWindow = true;
137                         psi.UseShellExecute = false;
138                         psi.RedirectStandardOutput = true;
139                         psi.RedirectStandardError = true;
140
141                         String result = "";
142
143                         using (Process proc = Process.Start(psi)) {
144                                 proc.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs args) {
145                                         result += args.Data + "\r\n";
146                                 };
147                                 proc.OutputDataReceived += delegate(object sender, DataReceivedEventArgs args) {
148                                         result += args.Data + "\r\n";
149                                 };
150                                 proc.BeginOutputReadLine();
151                                 proc.BeginErrorReadLine();
152                                 proc.WaitForExit();
153
154                                 output = result;
155
156                                 if (proc.ExitCode != 0)
157                                         return false;
158                         }
159
160                         return true;
161                 }
162
163                 private void VerifyCertificate(string host, string port)
164                 {
165                         SetRetrievalStatus(25);
166
167                         string pathPrefix = Icinga2InstallDir + "\\etc\\icinga2\\pki\\" + txtInstanceName.Text;
168
169                         string output;
170
171                         if (!File.Exists(pathPrefix + ".crt")) {
172                                 if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
173                                         "pki new-cert --cn \"" + txtInstanceName.Text + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\"",
174                                         out output)) {
175                                         ShowErrorText(output);
176                                         return;
177                                 }
178                         }
179
180                         SetRetrievalStatus(50);
181
182                         _TrustedFile = Path.GetTempFileName();
183
184                         if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
185                                 "pki save-cert --host \"" + host + "\" --port \"" + port + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\" --trustedcert \"" + _TrustedFile + "\"",
186                                 out output)) {
187                                 ShowErrorText(output);
188                                 return;
189                         }
190
191                         SetRetrievalStatus(100);
192
193                         X509Certificate2 cert = new X509Certificate2(_TrustedFile);
194                         Invoke((MethodInvoker)delegate { ShowCertificatePrompt(cert); });
195                 }
196
197                 private void ConfigureService()
198                 {
199                         SetConfigureStatus(0, "Updating configuration files...");
200
201                         string output;
202
203                         string args = "";
204
205                         if (rdoNewMaster.Checked)
206                                 args += " --master";
207
208                         Invoke((MethodInvoker)delegate {
209                                 string master_host, master_port;
210                                 GetMasterHostPort(out master_host, out master_port);
211
212                                 args += " --master_host " + master_host + "," + master_port;
213
214                                 foreach (ListViewItem lvi in lvwEndpoints.Items) {
215                                         args += " --endpoint " + lvi.SubItems[0].Text;
216                                         
217                                         if (lvi.SubItems.Count > 1)
218                                                 args += "," + lvi.SubItems[1].Text + "," + lvi.SubItems[2].Text;
219                                 }
220                         });
221
222                         if (rdoListener.Checked)
223                                 args += " --listen ::," + txtListenerPort.Text;
224
225                         if (chkAcceptConfig.Checked)
226                                 args += " --accept-config";
227
228                         if (chkAcceptCommands.Checked)
229                                 args += " --accept-commands";
230
231                         args += " --ticket " + txtTicket.Text;
232                         args += " --trustedcert " + _TrustedFile;
233                         args += " --cn " + txtInstanceName.Text;
234                         args += " --zone " + txtInstanceName.Text;
235
236                         if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
237                                 "node setup" + args,
238                                 out output)) {
239                                 ShowErrorText(output);
240                                 return;
241                         }
242
243                         SetConfigureStatus(50, "Setting ACLs for the Icinga 2 directory...");
244                         DirectoryInfo di = new DirectoryInfo(Icinga2InstallDir);
245                         DirectorySecurity ds = di.GetAccessControl();
246                         FileSystemAccessRule rule = new FileSystemAccessRule("NT AUTHORITY\\NetworkService",
247                                 FileSystemRights.Modify,
248                                 InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
249                         ds.AddAccessRule(rule);
250                         di.SetAccessControl(ds);
251
252                         SetConfigureStatus(75, "Installing the Icinga 2 service...");
253
254                         RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
255                                 "--scm-uninstall",
256                                 out output);
257
258                         if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
259                                 "daemon --validate",
260                                 out output)) {
261                                 ShowErrorText(output);
262                                 return;
263                         }
264
265                         if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
266                                 "--scm-install daemon",
267                                 out output)) {
268                                 ShowErrorText(output);
269                                 return;
270                         }
271
272                         if (chkInstallNSCP.Checked)
273                         {
274                                 SetConfigureStatus(85, "Waiting for NSClient++ installation to complete...");
275
276                                 Process proc = new Process();
277                                 proc.StartInfo.FileName = "msiexec.exe";
278                                 proc.StartInfo.Arguments = "/i \"" + Program.Icinga2InstallDir + "\\sbin\\NSCP-Win32.msi\"";
279                                 proc.Start();
280                                 proc.WaitForExit();
281                         }
282
283                         SetConfigureStatus(100, "Finished.");
284
285                         FinishConfigure();
286                 }
287
288                 private void FinishConfigure()
289                 {
290                         if (InvokeRequired) {
291                                 Invoke((MethodInvoker)FinishConfigure);
292                                 return;
293                         }
294
295                         tbcPages.SelectedTab = tabFinish;
296                 }
297
298                 private void AgentWizard_Shown(object sender, EventArgs e)
299                 {
300                         string installDir = Icinga2InstallDir;
301
302                         if (installDir == "")
303                                 FatalError("Icinga 2 does not seem to be installed properly.");
304
305                         /* TODO: This is something the NSIS installer should do */
306                         Directory.CreateDirectory(installDir + "\\etc\\icinga2\\pki");
307                         Directory.CreateDirectory(installDir + "\\var\\cache\\icinga2");
308                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\pki");
309                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\agent\\inventory");
310                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\config");
311                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\log");
312                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\zones");
313                         Directory.CreateDirectory(installDir + "\\var\\log\\icinga2\\compat\\archive");
314                         Directory.CreateDirectory(installDir + "\\var\\log\\icinga2\\crash");
315                         Directory.CreateDirectory(installDir + "\\var\\run\\icinga2\\cmd");
316                         Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\perfdata");
317                         Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\tmp");
318                 }
319
320                 private void btnBack_Click(object sender, EventArgs e)
321                 {
322                         if (tbcPages.SelectedTab == tabError) {
323                                 tbcPages.SelectedIndex = 0;
324                                 return;
325                         }
326
327                         int offset = 1;
328
329                         if (tbcPages.SelectedTab == tabVerifyCertificate)
330                                 offset++;
331
332                         tbcPages.SelectedIndex -= offset;
333                 }
334
335                 private void btnNext_Click(object sender, EventArgs e)
336                 {
337                         if (tbcPages.SelectedTab == tabParameters) {
338                                 if (txtInstanceName.Text.Length == 0) {
339                                         Warning("Please enter an instance name.");
340                                         return;
341                                 }
342
343                                 if (txtTicket.Text.Length == 0) {
344                                         Warning("Please enter an agent ticket.");
345                                         return;
346                                 }
347
348                                 if (rdoNoMaster.Checked) {
349                                         if (lvwEndpoints.Items.Count == 0) {
350                                                 Warning("You need to add at least one master endpoint.");
351                                                 return;
352                                         }
353
354                                         string host, port;
355                                         if (!GetMasterHostPort(out host, out port)) {
356                                                 Warning("Please enter a remote host and port for at least one of your endpoints.");
357                                                 return;
358                                         }
359                                 }
360
361                                 if (rdoListener.Checked && (txtListenerPort.Text == "")) {
362                                         Warning("You need to specify a listener port.");
363                                         return;
364                                 }
365                         }
366
367                         if (tbcPages.SelectedTab == tabFinish || tbcPages.SelectedTab == tabError)
368                                 Application.Exit();
369
370                         tbcPages.SelectedIndex++;
371                 }
372
373                 private void btnCancel_Click(object sender, EventArgs e)
374                 {
375                         Application.Exit();
376                 }
377
378                 private void tbcPages_SelectedIndexChanged(object sender, EventArgs e)
379                 {
380                         Refresh();
381
382                         btnBack.Enabled = (tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabError);
383                         btnNext.Enabled = (tbcPages.SelectedTab == tabParameters || tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabFinish);
384
385                         if (tbcPages.SelectedTab == tabFinish) {
386                                 btnNext.Text = "&Finish >";
387                                 btnCancel.Enabled = false;
388                         }
389
390                         if (tbcPages.SelectedTab == tabRetrieveCertificate) {
391                                 ListViewItem lvi = lvwEndpoints.Items[0];
392
393                                 string master_host, master_port;
394                                 GetMasterHostPort(out master_host, out master_port);
395
396                                 Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(master_host, master_port); });
397                                 thread.Start();
398                         }
399
400                         /*if (tbcPages.SelectedTab == tabParameters &&
401                                 !File.Exists(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent\\agent.crt")) {
402                                 byte[] bytes = Convert.FromBase64String(txtBundle.Text);
403                                 MemoryStream ms = new MemoryStream(bytes);
404                                 GZipStream gz = new GZipStream(ms, CompressionMode.Decompress);
405                                 MemoryStream ms2 = new MemoryStream();
406
407                                 byte[] buffer = new byte[512];
408                                 int rc;
409                                 while ((rc = gz.Read(buffer, 0, buffer.Length)) > 0)
410                                         ms2.Write(buffer, 0, rc);
411                                 ms2.Position = 0;
412                                 TarReader tr = new TarReader(ms2);
413                                 tr.ReadToEnd(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent");
414                         }*/
415
416                         if (tbcPages.SelectedTab == tabConfigure) {
417                                 Thread thread = new Thread(ConfigureService);
418                                 thread.Start();
419                         }
420                 }
421
422                 private void RadioMaster_CheckedChanged(object sender, EventArgs e)
423                 {
424                         lvwEndpoints.Enabled = !rdoNewMaster.Checked;
425                         btnAddEndpoint.Enabled = !rdoNewMaster.Checked;
426                         btnRemoveEndpoint.Enabled = !rdoNewMaster.Checked && lvwEndpoints.SelectedItems.Count > 0;
427                 }
428
429                 private void RadioListener_CheckedChanged(object sender, EventArgs e)
430                 {
431                         txtListenerPort.Enabled = rdoListener.Checked;
432                 }
433
434                 private void AddCertificateField(string name, string shortValue, string longValue = null)
435                 {
436                         ListViewItem lvi = new ListViewItem();
437                         lvi.Text = name;
438                         lvi.SubItems.Add(shortValue);
439                         if (longValue == null)
440                                 longValue = shortValue;
441                         lvi.Tag = longValue;
442                         lvwX509Fields.Items.Add(lvi);
443                 }
444
445                 private string PadText(string input)
446                 {
447                         string output = "";
448
449                         for (int i = 0; i < input.Length; i += 2) {
450                                 if (output != "")
451                                         output += " ";
452
453                                 int len = 2;
454                                 if (input.Length - i < 2)
455                                         len = input.Length - i;
456                                 output += input.Substring(i, len);
457                         }
458
459                         return output;
460                 }
461
462                 private void ShowCertificatePrompt(X509Certificate2 certificate)
463                 {
464                         txtX509Issuer.Text = certificate.Issuer;
465                         txtX509Subject.Text = certificate.Subject;
466
467                         lvwX509Fields.Items.Clear();
468
469                         AddCertificateField("Version", "V" + certificate.Version.ToString());
470                         AddCertificateField("Serial number", certificate.SerialNumber);
471                         AddCertificateField("Signature algorithm", certificate.SignatureAlgorithm.FriendlyName);
472                         AddCertificateField("Valid from", certificate.NotBefore.ToString());
473                         AddCertificateField("Valid to", certificate.NotAfter.ToString());
474
475                         string pkey = BitConverter.ToString(certificate.PublicKey.EncodedKeyValue.RawData).Replace("-", " ");
476                         AddCertificateField("Public key", certificate.PublicKey.Oid.FriendlyName + " (" + certificate.PublicKey.Key.KeySize + " bits)", pkey);
477
478                         string thumbprint = PadText(certificate.Thumbprint);
479                         AddCertificateField("Thumbprint", thumbprint);
480
481                         tbcPages.SelectedTab = tabVerifyCertificate;
482                 }
483
484                 private void btnAddEndpoint_Click(object sender, EventArgs e)
485                 {
486                         EndpointInputBox eib = new EndpointInputBox();
487
488                         if (eib.ShowDialog(this) == DialogResult.Cancel)
489                                 return;
490
491                         ListViewItem lvi = new ListViewItem();
492                         lvi.Text = eib.txtInstanceName.Text;
493
494                         if (eib.chkConnect.Checked) {
495                                 lvi.SubItems.Add(eib.txtHost.Text);
496                                 lvi.SubItems.Add(eib.txtPort.Text);
497                         }
498
499                         lvwEndpoints.Items.Add(lvi);
500                 }
501
502                 private void lvwEndpoints_SelectedIndexChanged(object sender, EventArgs e)
503                 {
504                         btnRemoveEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
505                 }
506
507                 private void lvwX509Fields_SelectedIndexChanged(object sender, EventArgs e)
508                 {
509                         if (lvwX509Fields.SelectedItems.Count == 0)
510                                 return;
511
512                         ListViewItem lvi = lvwX509Fields.SelectedItems[0];
513
514                         txtX509Field.Text = (string)lvi.Tag;
515                 }
516
517                 private void btnRemoveEndpoint_Click(object sender, EventArgs e)
518                 {
519                         while (lvwEndpoints.SelectedItems.Count > 0) {
520                                 lvwEndpoints.Items.Remove(lvwEndpoints.SelectedItems[0]);
521                         }
522         }
523         }
524 }