]> granicus.if.org Git - icinga2/blob - agent/windows-setup-agent/SetupWizard.cs
Merge pull request #7065 from uubk/logrotate-fix
[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 System.IO.Compression;
12 using System.Diagnostics;
13 using System.ServiceProcess;
14 using System.Security.AccessControl;
15
16 namespace Icinga
17 {
18         public partial class SetupWizard : Form
19         {
20                 private string _TrustedFile;
21                 private string Icinga2User;
22
23                 public SetupWizard()
24                 {
25                         InitializeComponent();
26
27                         txtInstanceName.Text = Icinga2InstanceName;
28
29                         Icinga2User = Program.Icinga2User;
30                         txtUser.Text = Icinga2User;
31                 }
32
33                 private void Warning(string message)
34                 {
35                         MessageBox.Show(this, message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
36                 }
37
38                 private string Icinga2InstanceName
39                 {
40                         get
41                         {
42                                 IPGlobalProperties props = IPGlobalProperties.GetIPGlobalProperties();
43
44                                 string fqdn = props.HostName;
45
46                                 if (props.DomainName != "")
47                                         fqdn += "." + props.DomainName;
48
49                                 return fqdn;
50                         }
51                 }
52
53                 private bool GetMasterHostPort(out string host, out string port)
54                 {
55                         foreach (ListViewItem lvi in lvwEndpoints.Items) {
56                                 if (lvi.SubItems.Count > 1) {
57                                         host = lvi.SubItems[1].Text.Trim();
58                                         port = lvi.SubItems[2].Text.Trim();
59                                         return true;
60                                 }
61                         }
62
63                         host = null;
64                         port = null;
65                         return false;
66                 }
67
68                 private void EnableFeature(string feature)
69                 {
70                         FileStream fp = null;
71                         try {
72                                 fp = File.Open(Program.Icinga2DataDir + String.Format("\\etc\\icinga2\\features-enabled\\{0}.conf", feature), FileMode.Create);
73                                 using (StreamWriter sw = new StreamWriter(fp, Encoding.ASCII)) {
74                                         fp = null;
75                                         sw.Write(String.Format("include \"../features-available/{0}.conf\"\n", feature));
76                                 }
77                         } finally {
78                                 if (fp != null)
79                                         fp.Dispose();
80                         }
81                 }
82
83                 private void SetRetrievalStatus(int pct)
84                 {
85                         if (InvokeRequired) {
86                                 Invoke((MethodInvoker)delegate { SetRetrievalStatus(pct); });
87                                 return;
88                         }
89
90                         prgRetrieveCertificate.Value = pct;
91                 }
92
93                 private void SetConfigureStatus(int pct, string message)
94                 {
95                         if (InvokeRequired) {
96                                 Invoke((MethodInvoker)delegate { SetConfigureStatus(pct, message); });
97                                 return;
98                         }
99
100                         prgConfig.Value = pct;
101                         lblConfigStatus.Text = message;
102                 }
103
104                 private void ShowErrorText(string text)
105                 {
106                         if (InvokeRequired) {
107                                 Invoke((MethodInvoker)delegate { ShowErrorText(text); });
108                                 return;
109                         }
110
111                         txtError.Text = text;
112                         tbcPages.SelectedTab = tabError;
113                 }
114
115                 private bool RunProcess(string filename, string arguments, out string output)
116                 {
117                         ProcessStartInfo psi = new ProcessStartInfo();
118                         psi.FileName = filename;
119                         psi.Arguments = arguments;
120                         psi.CreateNoWindow = true;
121                         psi.UseShellExecute = false;
122                         psi.RedirectStandardOutput = true;
123                         psi.RedirectStandardError = true;
124
125                         String result = "";
126
127                         using (Process proc = Process.Start(psi)) {
128                                 proc.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs args)
129                                 {
130                                         result += args.Data + "\r\n";
131                                 };
132                                 proc.OutputDataReceived += delegate (object sender, DataReceivedEventArgs args)
133                                 {
134                                         result += args.Data + "\r\n";
135                                 };
136                                 proc.BeginOutputReadLine();
137                                 proc.BeginErrorReadLine();
138                                 proc.WaitForExit();
139
140                                 output = result;
141
142                                 if (proc.ExitCode != 0)
143                                         return false;
144                         }
145
146                         return true;
147                 }
148
149                 private void VerifyCertificate(string host, string port)
150                 {
151                         SetRetrievalStatus(25);
152
153                         string pathPrefix = Program.Icinga2DataDir + "\\etc\\icinga2\\pki\\" + txtInstanceName.Text;
154                         string processArguments = "pki new-cert --cn \"" + txtInstanceName.Text + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\"";
155                         string output;
156
157                         if (!File.Exists(pathPrefix + ".crt")) {
158                                 if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
159                                         processArguments,
160                                         out output)) {
161                                         ShowErrorText("Running command 'icinga2.exe " + processArguments + "' produced the following output:\n" + output);
162                                         return;
163                                 }
164                         }
165
166                         SetRetrievalStatus(50);
167
168                         _TrustedFile = Path.GetTempFileName();
169
170                         processArguments = "pki save-cert --host \"" + host + "\" --port \"" + port + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\" --trustedcert \"" + _TrustedFile + "\"";
171                         if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
172                                 processArguments,
173                                 out output)) {
174                                 ShowErrorText("Running command 'icinga2.exe " + processArguments + "' produced the following output:\n" + output);
175                                 return;
176                         }
177
178                         SetRetrievalStatus(100);
179                         try {
180                                 X509Certificate2 cert = new X509Certificate2(_TrustedFile);
181                                 Invoke((MethodInvoker)delegate { ShowCertificatePrompt(cert); });
182                         } catch (Exception e) {
183                                 ShowErrorText("Failed to receive certificate: " + e.Message);
184                         }
185                 }
186
187                 private void ConfigureService()
188                 {
189                         SetConfigureStatus(0, "Updating configuration files...");
190
191                         string output;
192
193                         string args = "";
194
195                         Invoke((MethodInvoker)delegate
196                         {
197                                 string master_host, master_port;
198                                 GetMasterHostPort(out master_host, out master_port);
199
200                                 args += " --master_host " + master_host + "," + master_port;
201
202                                 foreach (ListViewItem lvi in lvwEndpoints.Items) {
203                                         args += " --endpoint " + lvi.SubItems[0].Text.Trim();
204
205                                         if (lvi.SubItems.Count > 1)
206                                                 args += "," + lvi.SubItems[1].Text.Trim() + "," + lvi.SubItems[2].Text.Trim();
207                                 }
208                         });
209
210                         if (rdoListener.Checked)
211                                 args += " --listen ::," + txtListenerPort.Text.Trim();
212
213                         if (chkAcceptConfig.Checked)
214                                 args += " --accept-config";
215
216                         if (chkAcceptCommands.Checked)
217                                 args += " --accept-commands";
218
219                         string ticket = txtTicket.Text.Trim();
220
221                         if (ticket.Length > 0)
222                                 args += " --ticket \"" + ticket + "\"";
223
224                         args += " --trustedcert \"" + _TrustedFile + "\"";
225                         args += " --cn \"" + txtInstanceName.Text.Trim() + "\"";
226                         args += " --zone \"" + txtInstanceName.Text.Trim() + "\"";
227
228                         foreach (ListViewItem lvi in lvwGlobalZones.Items) {
229                                 args += " --global_zones " + lvi.SubItems[0].Text.Trim();
230                         }
231
232                         if (chkDisableConf.Checked)
233                                 args += " --disable-confd";
234
235                         if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
236                                 "node setup" + args,
237                                 out output)) {
238                                 ShowErrorText("Running command 'icinga2.exe " + "node setup" + args + "' produced the following output:\n" + output);
239                                 return;
240                         }
241
242                         SetConfigureStatus(50, "Setting ACLs for the Icinga 2 directory...");
243
244                         string serviceUser = txtUser.Text.Trim();
245
246                         DirectoryInfo di = new DirectoryInfo(Program.Icinga2InstallDir);
247                         DirectorySecurity ds = di.GetAccessControl();
248                         FileSystemAccessRule rule = new FileSystemAccessRule(serviceUser,
249                                 FileSystemRights.Modify,
250                                 InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
251                         try {
252                                 ds.AddAccessRule(rule);
253                                 di.SetAccessControl(ds);
254                         } catch (System.Security.Principal.IdentityNotMappedException) {
255                                 ShowErrorText("Could not set ACLs for user \"" + serviceUser + "\". Identitiy is not mapped.\n");
256                                 return;
257                         }
258
259                         SetConfigureStatus(75, "Installing the Icinga 2 service...");
260
261                         RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
262                                 "--scm-uninstall",
263                                 out output);
264
265                         if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
266                                 "daemon --validate",
267                                 out output)) {
268                                 ShowErrorText("Running command 'icinga2.exe daemon --validate' produced the following output:\n" + output);
269                                 return;
270                         }
271
272                         if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
273                                 "--scm-install --scm-user \"" + serviceUser + "\" daemon",
274                                 out output)) {
275                                 ShowErrorText("\nRunning command 'icinga2.exe --scm-install --scm-user \"" +
276                                         serviceUser + "\" daemon' produced the following output:\n" + output);
277                                 return;
278                         }
279
280                         if (chkInstallNSCP.Checked) {
281                                 SetConfigureStatus(85, "Waiting for NSClient++ installation to complete...");
282
283                                 Process proc = new Process();
284                                 proc.StartInfo.FileName = "msiexec.exe";
285                                 proc.StartInfo.Arguments = "/i \"" + Program.Icinga2InstallDir + "\\sbin\\NSCP.msi\"";
286                                 proc.Start();
287                                 proc.WaitForExit();
288                         }
289
290                         SetConfigureStatus(100, "Finished.");
291
292                         // Override the completed text
293                         lblSetupCompleted.Text = "The Icinga 2 Windows client was set up successfully.";
294
295                         // Add a note for the user for ticket-less signing
296                         if (ticket.Length == 0) {
297                                 lblSetupCompleted.Text += "\n\nTicket was not specified. Please sign the certificate request on the Icinga 2 master node (requires v2.8+).";
298                         }
299
300                         FinishConfigure();
301                 }
302
303                 private void FinishConfigure()
304                 {
305                         if (InvokeRequired) {
306                                 Invoke((MethodInvoker)FinishConfigure);
307                                 return;
308                         }
309
310                         tbcPages.SelectedTab = tabFinish;
311                 }
312
313                 private void btnBack_Click(object sender, EventArgs e)
314                 {
315                         if (tbcPages.SelectedTab == tabError) {
316                                 tbcPages.SelectedIndex = 0;
317                                 return;
318                         }
319
320                         int offset = 1;
321
322                         if (tbcPages.SelectedTab == tabVerifyCertificate)
323                                 offset++;
324
325                         tbcPages.SelectedIndex -= offset;
326                 }
327
328                 private void btnNext_Click(object sender, EventArgs e)
329                 {
330                         if (tbcPages.SelectedTab == tabParameters) {
331                                 if (txtInstanceName.Text.Length == 0) {
332                                         Warning("Please enter an instance name.");
333                                         return;
334                                 }
335
336                                 if (lvwEndpoints.Items.Count == 0) {
337                                         Warning("You need to add at least one master/satellite endpoint.");
338                                         return;
339                                 }
340
341                                 string host, port;
342                                 if (!GetMasterHostPort(out host, out port)) {
343                                         Warning("Please enter a remote host and port for at least one of your endpoints.");
344                                         return;
345                                 }
346
347                                 if (rdoListener.Checked && (txtListenerPort.Text == "")) {
348                                         Warning("You need to specify a listener port.");
349                                         return;
350                                 }
351
352                                 if (txtUser.Text.Length == 0) {
353                                         Warning("Icinga 2 service user may not be empty.");
354                                         return;
355                                 }
356                         }
357
358                         if (tbcPages.SelectedTab == tabFinish || tbcPages.SelectedTab == tabError)
359                                 Application.Exit();
360
361                         tbcPages.SelectedIndex++;
362                 }
363
364                 private void btnCancel_Click(object sender, EventArgs e)
365                 {
366                         Application.Exit();
367                 }
368
369                 private void tbcPages_SelectedIndexChanged(object sender, EventArgs e)
370                 {
371                         Refresh();
372
373                         btnBack.Enabled = (tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabError);
374                         btnNext.Enabled = (tbcPages.SelectedTab == tabParameters || tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabFinish);
375
376                         if (tbcPages.SelectedTab == tabFinish) {
377                                 btnNext.Text = "&Finish >";
378                                 btnCancel.Enabled = false;
379                         }
380
381                         if (tbcPages.SelectedTab == tabRetrieveCertificate) {
382                                 ListViewItem lvi = lvwEndpoints.Items[0];
383
384                                 string master_host, master_port;
385                                 GetMasterHostPort(out master_host, out master_port);
386
387                                 Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(master_host, master_port); });
388                                 thread.Start();
389                         }
390
391                         if (tbcPages.SelectedTab == tabConfigure) {
392                                 Thread thread = new Thread(ConfigureService);
393                                 thread.Start();
394                         }
395                 }
396
397                 private void RadioListener_CheckedChanged(object sender, EventArgs e)
398                 {
399                         txtListenerPort.Enabled = rdoListener.Checked;
400                 }
401
402                 private void AddCertificateField(string name, string shortValue, string longValue = null)
403                 {
404                         ListViewItem lvi = new ListViewItem();
405                         lvi.Text = name;
406                         lvi.SubItems.Add(shortValue);
407                         if (longValue == null)
408                                 longValue = shortValue;
409                         lvi.Tag = longValue;
410                         lvwX509Fields.Items.Add(lvi);
411                 }
412
413                 private string PadText(string input)
414                 {
415                         string output = "";
416
417                         for (int i = 0; i < input.Length; i += 2) {
418                                 if (output != "")
419                                         output += " ";
420
421                                 int len = 2;
422                                 if (input.Length - i < 2)
423                                         len = input.Length - i;
424                                 output += input.Substring(i, len);
425                         }
426
427                         return output;
428                 }
429
430                 private void ShowCertificatePrompt(X509Certificate2 certificate)
431                 {
432                         txtX509Issuer.Text = certificate.Issuer;
433                         txtX509Subject.Text = certificate.Subject;
434
435                         lvwX509Fields.Items.Clear();
436
437                         AddCertificateField("Version", "V" + certificate.Version.ToString());
438                         AddCertificateField("Serial number", certificate.SerialNumber);
439                         AddCertificateField("Signature algorithm", certificate.SignatureAlgorithm.FriendlyName);
440                         AddCertificateField("Valid from", certificate.NotBefore.ToString());
441                         AddCertificateField("Valid to", certificate.NotAfter.ToString());
442
443                         string pkey = BitConverter.ToString(certificate.PublicKey.EncodedKeyValue.RawData).Replace("-", " ");
444                         AddCertificateField("Public key", certificate.PublicKey.Oid.FriendlyName + " (" + certificate.PublicKey.Key.KeySize + " bits)", pkey);
445
446                         string thumbprint = PadText(certificate.Thumbprint);
447                         AddCertificateField("Thumbprint", thumbprint);
448
449                         tbcPages.SelectedTab = tabVerifyCertificate;
450                 }
451
452                 private void btnAddEndpoint_Click(object sender, EventArgs e)
453                 {
454                         EndpointInputBox eib = new EndpointInputBox();
455
456                         if (eib.ShowDialog(this) == DialogResult.Cancel)
457                                 return;
458
459                         ListViewItem lvi = new ListViewItem();
460                         lvi.Text = eib.txtInstanceName.Text;
461
462                         if (eib.chkConnect.Checked) {
463                                 lvi.SubItems.Add(eib.txtHost.Text);
464                                 lvi.SubItems.Add(eib.txtPort.Text);
465                         }
466
467                         lvwEndpoints.Items.Add(lvi);
468                 }
469
470                 private void lvwEndpoints_SelectedIndexChanged(object sender, EventArgs e)
471                 {
472                         btnRemoveEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
473                         btnEditEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
474                 }
475
476                 private void lvwX509Fields_SelectedIndexChanged(object sender, EventArgs e)
477                 {
478                         if (lvwX509Fields.SelectedItems.Count == 0)
479                                 return;
480
481                         ListViewItem lvi = lvwX509Fields.SelectedItems[0];
482
483                         txtX509Field.Text = Convert.ToString(lvi.Tag);
484                 }
485
486                 private void btnRemoveEndpoint_Click(object sender, EventArgs e)
487                 {
488                         while (lvwEndpoints.SelectedItems.Count > 0) {
489                                 lvwEndpoints.Items.Remove(lvwEndpoints.SelectedItems[0]);
490                         }
491                 }
492
493                 private void chkRunServiceAsThisUser_CheckedChanged(object sender, EventArgs e)
494                 {
495                         txtUser.Enabled = !txtUser.Enabled;
496                         if (!txtUser.Enabled)
497                                 txtUser.Text = Icinga2User;
498                 }
499
500                 private void btnEditEndpoint_Click(object sender, EventArgs e)
501                 {
502                         ListViewItem lvi = lvwEndpoints.SelectedItems[0];
503                         EndpointInputBox eib = new EndpointInputBox();
504
505                         eib.Text = "Edit Endpoint";
506                         eib.txtInstanceName.Text = lvi.SubItems[0].Text;
507
508                         if (lvi.SubItems.Count >= 2) {
509                                 eib.txtHost.Text = lvi.SubItems[1].Text;
510                                 eib.txtPort.Text = lvi.SubItems[2].Text;
511                                 eib.chkConnect.Checked = true;
512                         }
513
514                         if (eib.ShowDialog(this) == DialogResult.Cancel)
515                                 return;
516
517                         lvwEndpoints.Items.Remove(lvi);
518
519                         ListViewItem lvi2 = new ListViewItem();
520                         lvi2.Text = eib.txtInstanceName.Text;
521
522                         if (eib.chkConnect.Checked) {
523                                 lvi2.SubItems.Add(eib.txtHost.Text);
524                                 lvi2.SubItems.Add(eib.txtPort.Text);
525                         }
526
527                         lvwEndpoints.Items.Add(lvi2);
528                 }
529
530                 private void btnAddGlobalZone_Click(object sender, EventArgs e)
531                 {
532                         GlobalZonesInputBox gzib = new GlobalZonesInputBox(lvwGlobalZones.Items);
533
534                         if (gzib.ShowDialog(this) == DialogResult.Cancel)
535                                 return;
536
537                         ListViewItem lvi = new ListViewItem();
538                         lvi.Text = gzib.txtGlobalZoneName.Text;
539
540                         lvwGlobalZones.Items.Add(lvi);
541                 }
542
543                 private void btnRemoveGlobalZone_Click(object sender, EventArgs e)
544                 {
545                         while (lvwGlobalZones.SelectedItems.Count > 0) {
546                                 lvwGlobalZones.Items.Remove(lvwGlobalZones.SelectedItems[0]);
547                         }
548                 }
549
550                 private void lvwGlobalZones_SelectedIndexChanged(object sender, EventArgs e)
551                 {
552                         btnEditGlobalZone.Enabled = lvwGlobalZones.SelectedItems.Count > 0;
553                         btnRemoveGlobalZone.Enabled = lvwGlobalZones.SelectedItems.Count > 0;
554                 }
555
556                 private void btnEditGlobalZone_Click(object sender, EventArgs e)
557                 {
558                         ListViewItem lvi = lvwGlobalZones.SelectedItems[0];
559                         GlobalZonesInputBox gzib = new GlobalZonesInputBox(lvwGlobalZones.Items);
560
561                         gzib.Text = "Edit Global Zone";
562                         gzib.txtGlobalZoneName.Text = lvi.SubItems[0].Text;
563                         
564                         if (gzib.ShowDialog(this) == DialogResult.Cancel)
565                                 return;
566
567                         lvwGlobalZones.Items.Remove(lvi);
568
569                         ListViewItem lvi2 = new ListViewItem();
570                         lvi2.Text = gzib.txtGlobalZoneName.Text;
571                         
572                         lvwGlobalZones.Items.Add(lvi2);
573                 }
574
575                 private void checkBox1_CheckedChanged(object sender, EventArgs e)
576                 {
577
578                 }
579
580                 private void SetupWizard_Load(object sender, EventArgs e)
581                 {
582                         this.MinimumSize = this.Size;
583                         this.MaximumSize = this.Size;
584                 }
585         }
586 }
587