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