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