]> granicus.if.org Git - icinga2/blob - agent/windows-setup-agent/SetupWizard.cs
Add missing directory in the Windows setup wizard
[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                         args += " --ticket " + txtTicket.Text;
226                         args += " --trustedcert " + _TrustedFile;
227                         args += " --cn " + txtInstanceName.Text;
228                         args += " --zone " + txtInstanceName.Text;
229
230                         if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
231                                 "node setup" + args,
232                                 out output)) {
233                                 ShowErrorText(output);
234                                 return;
235                         }
236
237                         SetConfigureStatus(50, "Setting ACLs for the Icinga 2 directory...");
238                         DirectoryInfo di = new DirectoryInfo(Icinga2InstallDir);
239                         DirectorySecurity ds = di.GetAccessControl();
240                         FileSystemAccessRule rule = new FileSystemAccessRule("NT AUTHORITY\\NetworkService",
241                                 FileSystemRights.Modify,
242                                 InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
243                         ds.AddAccessRule(rule);
244                         di.SetAccessControl(ds);
245
246                         SetConfigureStatus(75, "Installing the Icinga 2 service...");
247
248                         RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
249                                 "--scm-uninstall",
250                                 out output);
251
252                         if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
253                                 "daemon --validate",
254                                 out output)) {
255                                 ShowErrorText(output);
256                                 return;
257                         }
258
259                         if (!RunProcess(Icinga2InstallDir + "\\sbin\\icinga2.exe",
260                                 "--scm-install daemon",
261                                 out output)) {
262                                 ShowErrorText(output);
263                                 return;
264                         }
265
266                         SetConfigureStatus(100, "Finished.");
267
268                         FinishConfigure();
269                 }
270
271                 private void FinishConfigure()
272                 {
273                         if (InvokeRequired) {
274                                 Invoke((MethodInvoker)FinishConfigure);
275                                 return;
276                         }
277
278                         tbcPages.SelectedTab = tabFinish;
279                 }
280
281                 private void AgentWizard_Shown(object sender, EventArgs e)
282                 {
283                         string installDir = Icinga2InstallDir;
284
285                         if (installDir == "")
286                                 FatalError("Icinga 2 does not seem to be installed properly.");
287
288                         /* TODO: This is something the NSIS installer should do */
289                         Directory.CreateDirectory(installDir + "\\etc\\icinga2\\pki");
290                         Directory.CreateDirectory(installDir + "\\var\\cache\\icinga2");
291                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\pki");
292                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\agent\\inventory");
293                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\config");
294                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\log");
295                         Directory.CreateDirectory(installDir + "\\var\\lib\\icinga2\\api\\zones");
296                         Directory.CreateDirectory(installDir + "\\var\\log\\icinga2\\compat\\archive");
297                         Directory.CreateDirectory(installDir + "\\var\\run\\icinga2\\cmd");
298                         Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\perfdata");
299                         Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\tmp");
300                 }
301
302                 private void btnBack_Click(object sender, EventArgs e)
303                 {
304                         if (tbcPages.SelectedTab == tabError) {
305                                 tbcPages.SelectedIndex = 0;
306                                 return;
307                         }
308
309                         int offset = 1;
310
311                         if (tbcPages.SelectedTab == tabVerifyCertificate)
312                                 offset++;
313
314                         tbcPages.SelectedIndex -= offset;
315                 }
316
317                 private void btnNext_Click(object sender, EventArgs e)
318                 {
319                         if (tbcPages.SelectedTab == tabParameters) {
320                                 if (txtInstanceName.Text.Length == 0) {
321                                         Warning("Please enter an instance name.");
322                                         return;
323                                 }
324
325                                 if (txtTicket.Text.Length == 0) {
326                                         Warning("Please enter an agent ticket.");
327                                         return;
328                                 }
329
330                                 if (rdoNoMaster.Checked) {
331                                         if (lvwEndpoints.Items.Count == 0) {
332                                                 Warning("You need to add at least one master endpoint.");
333                                                 return;
334                                         }
335
336                                         string host, port;
337                                         if (!GetMasterHostPort(out host, out port)) {
338                                                 Warning("Please enter a remote host and port for at least one of your endpoints.");
339                                                 return;
340                                         }
341                                 }
342
343                                 if (rdoListener.Checked && (txtListenerPort.Text == "")) {
344                                         Warning("You need to specify a listener port.");
345                                         return;
346                                 }
347                         }
348
349                         if (tbcPages.SelectedTab == tabFinish || tbcPages.SelectedTab == tabError)
350                                 Application.Exit();
351
352                         tbcPages.SelectedIndex++;
353                 }
354
355                 private void btnCancel_Click(object sender, EventArgs e)
356                 {
357                         Application.Exit();
358                 }
359
360                 private void tbcPages_SelectedIndexChanged(object sender, EventArgs e)
361                 {
362                         Refresh();
363
364                         btnBack.Enabled = (tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabError);
365                         btnNext.Enabled = (tbcPages.SelectedTab == tabParameters || tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabFinish);
366
367                         if (tbcPages.SelectedTab == tabFinish) {
368                                 btnNext.Text = "&Finish >";
369                                 btnCancel.Enabled = false;
370                         }
371
372                         if (tbcPages.SelectedTab == tabRetrieveCertificate) {
373                                 ListViewItem lvi = lvwEndpoints.Items[0];
374
375                                 string master_host, master_port;
376                                 GetMasterHostPort(out master_host, out master_port);
377
378                                 Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(master_host, master_port); });
379                                 thread.Start();
380                         }
381
382                         /*if (tbcPages.SelectedTab == tabParameters &&
383                                 !File.Exists(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent\\agent.crt")) {
384                                 byte[] bytes = Convert.FromBase64String(txtBundle.Text);
385                                 MemoryStream ms = new MemoryStream(bytes);
386                                 GZipStream gz = new GZipStream(ms, CompressionMode.Decompress);
387                                 MemoryStream ms2 = new MemoryStream();
388
389                                 byte[] buffer = new byte[512];
390                                 int rc;
391                                 while ((rc = gz.Read(buffer, 0, buffer.Length)) > 0)
392                                         ms2.Write(buffer, 0, rc);
393                                 ms2.Position = 0;
394                                 TarReader tr = new TarReader(ms2);
395                                 tr.ReadToEnd(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent");
396                         }*/
397
398                         if (tbcPages.SelectedTab == tabConfigure) {
399                                 Thread thread = new Thread(ConfigureService);
400                                 thread.Start();
401                         }
402                 }
403
404                 private void RadioMaster_CheckedChanged(object sender, EventArgs e)
405                 {
406                         lvwEndpoints.Enabled = !rdoNewMaster.Checked;
407                         btnAddEndpoint.Enabled = !rdoNewMaster.Checked;
408                         btnRemoveEndpoint.Enabled = !rdoNewMaster.Checked && lvwEndpoints.SelectedItems.Count > 0;
409                 }
410
411                 private void RadioListener_CheckedChanged(object sender, EventArgs e)
412                 {
413                         txtListenerPort.Enabled = rdoListener.Checked;
414                 }
415
416                 private void AddCertificateField(string name, string shortValue, string longValue = null)
417                 {
418                         ListViewItem lvi = new ListViewItem();
419                         lvi.Text = name;
420                         lvi.SubItems.Add(shortValue);
421                         if (longValue == null)
422                                 longValue = shortValue;
423                         lvi.Tag = longValue;
424                         lvwX509Fields.Items.Add(lvi);
425                 }
426
427                 private string PadText(string input)
428                 {
429                         string output = "";
430
431                         for (int i = 0; i < input.Length; i += 2) {
432                                 if (output != "")
433                                         output += " ";
434
435                                 int len = 2;
436                                 if (input.Length - i < 2)
437                                         len = input.Length - i;
438                                 output += input.Substring(i, len);
439                         }
440
441                         return output;
442                 }
443
444                 private void ShowCertificatePrompt(X509Certificate2 certificate)
445                 {
446                         txtX509Issuer.Text = certificate.Issuer;
447                         txtX509Subject.Text = certificate.Subject;
448
449                         lvwX509Fields.Items.Clear();
450
451                         AddCertificateField("Version", "V" + certificate.Version.ToString());
452                         AddCertificateField("Serial number", certificate.SerialNumber);
453                         AddCertificateField("Signature algorithm", certificate.SignatureAlgorithm.FriendlyName);
454                         AddCertificateField("Valid from", certificate.NotBefore.ToString());
455                         AddCertificateField("Valid to", certificate.NotAfter.ToString());
456
457                         string pkey = BitConverter.ToString(certificate.PublicKey.EncodedKeyValue.RawData).Replace("-", " ");
458                         AddCertificateField("Public key", certificate.PublicKey.Oid.FriendlyName + " (" + certificate.PublicKey.Key.KeySize + " bits)", pkey);
459
460                         string thumbprint = PadText(certificate.Thumbprint);
461                         AddCertificateField("Thumbprint", thumbprint);
462
463                         tbcPages.SelectedTab = tabVerifyCertificate;
464                 }
465
466                 private void btnAddEndpoint_Click(object sender, EventArgs e)
467                 {
468                         EndpointInputBox eib = new EndpointInputBox();
469
470                         if (eib.ShowDialog(this) == DialogResult.Cancel)
471                                 return;
472
473                         ListViewItem lvi = new ListViewItem();
474                         lvi.Text = eib.txtInstanceName.Text;
475
476                         if (eib.chkConnect.Checked) {
477                                 lvi.SubItems.Add(eib.txtHost.Text);
478                                 lvi.SubItems.Add(eib.txtPort.Text);
479                         }
480
481                         lvwEndpoints.Items.Add(lvi);
482                 }
483
484                 private void lvwEndpoints_SelectedIndexChanged(object sender, EventArgs e)
485                 {
486                         btnRemoveEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
487                 }
488
489                 private void lvwX509Fields_SelectedIndexChanged(object sender, EventArgs e)
490                 {
491                         if (lvwX509Fields.SelectedItems.Count == 0)
492                                 return;
493
494                         ListViewItem lvi = lvwX509Fields.SelectedItems[0];
495
496                         txtX509Field.Text = (string)lvi.Tag;
497                 }
498
499                 private void btnRemoveEndpoint_Click(object sender, EventArgs e)
500                 {
501                         while (lvwEndpoints.SelectedItems.Count > 0) {
502                                 lvwEndpoints.Items.Remove(lvwEndpoints.SelectedItems[0]);
503                         }
504                 }
505         }
506 }