]> granicus.if.org Git - icinga2/blob - agent/windows-setup-agent/SetupWizard.cs
1b6bccc1f18ff8307ae62f4815b3e83aa35771a1
[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\\log\\icinga2\\crash");
298                         Directory.CreateDirectory(installDir + "\\var\\run\\icinga2\\cmd");
299                         Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\perfdata");
300                         Directory.CreateDirectory(installDir + "\\var\\spool\\icinga2\\tmp");
301                 }
302
303                 private void btnBack_Click(object sender, EventArgs e)
304                 {
305                         if (tbcPages.SelectedTab == tabError) {
306                                 tbcPages.SelectedIndex = 0;
307                                 return;
308                         }
309
310                         int offset = 1;
311
312                         if (tbcPages.SelectedTab == tabVerifyCertificate)
313                                 offset++;
314
315                         tbcPages.SelectedIndex -= offset;
316                 }
317
318                 private void btnNext_Click(object sender, EventArgs e)
319                 {
320                         if (tbcPages.SelectedTab == tabParameters) {
321                                 if (txtInstanceName.Text.Length == 0) {
322                                         Warning("Please enter an instance name.");
323                                         return;
324                                 }
325
326                                 if (txtTicket.Text.Length == 0) {
327                                         Warning("Please enter an agent ticket.");
328                                         return;
329                                 }
330
331                                 if (rdoNoMaster.Checked) {
332                                         if (lvwEndpoints.Items.Count == 0) {
333                                                 Warning("You need to add at least one master endpoint.");
334                                                 return;
335                                         }
336
337                                         string host, port;
338                                         if (!GetMasterHostPort(out host, out port)) {
339                                                 Warning("Please enter a remote host and port for at least one of your endpoints.");
340                                                 return;
341                                         }
342                                 }
343
344                                 if (rdoListener.Checked && (txtListenerPort.Text == "")) {
345                                         Warning("You need to specify a listener port.");
346                                         return;
347                                 }
348                         }
349
350                         if (tbcPages.SelectedTab == tabFinish || tbcPages.SelectedTab == tabError)
351                                 Application.Exit();
352
353                         tbcPages.SelectedIndex++;
354                 }
355
356                 private void btnCancel_Click(object sender, EventArgs e)
357                 {
358                         Application.Exit();
359                 }
360
361                 private void tbcPages_SelectedIndexChanged(object sender, EventArgs e)
362                 {
363                         Refresh();
364
365                         btnBack.Enabled = (tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabError);
366                         btnNext.Enabled = (tbcPages.SelectedTab == tabParameters || tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabFinish);
367
368                         if (tbcPages.SelectedTab == tabFinish) {
369                                 btnNext.Text = "&Finish >";
370                                 btnCancel.Enabled = false;
371                         }
372
373                         if (tbcPages.SelectedTab == tabRetrieveCertificate) {
374                                 ListViewItem lvi = lvwEndpoints.Items[0];
375
376                                 string master_host, master_port;
377                                 GetMasterHostPort(out master_host, out master_port);
378
379                                 Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(master_host, master_port); });
380                                 thread.Start();
381                         }
382
383                         /*if (tbcPages.SelectedTab == tabParameters &&
384                                 !File.Exists(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent\\agent.crt")) {
385                                 byte[] bytes = Convert.FromBase64String(txtBundle.Text);
386                                 MemoryStream ms = new MemoryStream(bytes);
387                                 GZipStream gz = new GZipStream(ms, CompressionMode.Decompress);
388                                 MemoryStream ms2 = new MemoryStream();
389
390                                 byte[] buffer = new byte[512];
391                                 int rc;
392                                 while ((rc = gz.Read(buffer, 0, buffer.Length)) > 0)
393                                         ms2.Write(buffer, 0, rc);
394                                 ms2.Position = 0;
395                                 TarReader tr = new TarReader(ms2);
396                                 tr.ReadToEnd(Icinga2InstallDir + "\\etc\\icinga2\\pki\\agent");
397                         }*/
398
399                         if (tbcPages.SelectedTab == tabConfigure) {
400                                 Thread thread = new Thread(ConfigureService);
401                                 thread.Start();
402                         }
403                 }
404
405                 private void RadioMaster_CheckedChanged(object sender, EventArgs e)
406                 {
407                         lvwEndpoints.Enabled = !rdoNewMaster.Checked;
408                         btnAddEndpoint.Enabled = !rdoNewMaster.Checked;
409                         btnRemoveEndpoint.Enabled = !rdoNewMaster.Checked && lvwEndpoints.SelectedItems.Count > 0;
410                 }
411
412                 private void RadioListener_CheckedChanged(object sender, EventArgs e)
413                 {
414                         txtListenerPort.Enabled = rdoListener.Checked;
415                 }
416
417                 private void AddCertificateField(string name, string shortValue, string longValue = null)
418                 {
419                         ListViewItem lvi = new ListViewItem();
420                         lvi.Text = name;
421                         lvi.SubItems.Add(shortValue);
422                         if (longValue == null)
423                                 longValue = shortValue;
424                         lvi.Tag = longValue;
425                         lvwX509Fields.Items.Add(lvi);
426                 }
427
428                 private string PadText(string input)
429                 {
430                         string output = "";
431
432                         for (int i = 0; i < input.Length; i += 2) {
433                                 if (output != "")
434                                         output += " ";
435
436                                 int len = 2;
437                                 if (input.Length - i < 2)
438                                         len = input.Length - i;
439                                 output += input.Substring(i, len);
440                         }
441
442                         return output;
443                 }
444
445                 private void ShowCertificatePrompt(X509Certificate2 certificate)
446                 {
447                         txtX509Issuer.Text = certificate.Issuer;
448                         txtX509Subject.Text = certificate.Subject;
449
450                         lvwX509Fields.Items.Clear();
451
452                         AddCertificateField("Version", "V" + certificate.Version.ToString());
453                         AddCertificateField("Serial number", certificate.SerialNumber);
454                         AddCertificateField("Signature algorithm", certificate.SignatureAlgorithm.FriendlyName);
455                         AddCertificateField("Valid from", certificate.NotBefore.ToString());
456                         AddCertificateField("Valid to", certificate.NotAfter.ToString());
457
458                         string pkey = BitConverter.ToString(certificate.PublicKey.EncodedKeyValue.RawData).Replace("-", " ");
459                         AddCertificateField("Public key", certificate.PublicKey.Oid.FriendlyName + " (" + certificate.PublicKey.Key.KeySize + " bits)", pkey);
460
461                         string thumbprint = PadText(certificate.Thumbprint);
462                         AddCertificateField("Thumbprint", thumbprint);
463
464                         tbcPages.SelectedTab = tabVerifyCertificate;
465                 }
466
467                 private void btnAddEndpoint_Click(object sender, EventArgs e)
468                 {
469                         EndpointInputBox eib = new EndpointInputBox();
470
471                         if (eib.ShowDialog(this) == DialogResult.Cancel)
472                                 return;
473
474                         ListViewItem lvi = new ListViewItem();
475                         lvi.Text = eib.txtInstanceName.Text;
476
477                         if (eib.chkConnect.Checked) {
478                                 lvi.SubItems.Add(eib.txtHost.Text);
479                                 lvi.SubItems.Add(eib.txtPort.Text);
480                         }
481
482                         lvwEndpoints.Items.Add(lvi);
483                 }
484
485                 private void lvwEndpoints_SelectedIndexChanged(object sender, EventArgs e)
486                 {
487                         btnRemoveEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
488                 }
489
490                 private void lvwX509Fields_SelectedIndexChanged(object sender, EventArgs e)
491                 {
492                         if (lvwX509Fields.SelectedItems.Count == 0)
493                                 return;
494
495                         ListViewItem lvi = lvwX509Fields.SelectedItems[0];
496
497                         txtX509Field.Text = (string)lvi.Tag;
498                 }
499
500                 private void btnRemoveEndpoint_Click(object sender, EventArgs e)
501                 {
502                         while (lvwEndpoints.SelectedItems.Count > 0) {
503                                 lvwEndpoints.Items.Remove(lvwEndpoints.SelectedItems[0]);
504                         }
505                 }
506         }
507 }