summaryrefslogtreecommitdiffstats
path: root/agent/windows-setup-agent/SetupWizard.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--agent/windows-setup-agent/SetupWizard.cs574
1 files changed, 574 insertions, 0 deletions
diff --git a/agent/windows-setup-agent/SetupWizard.cs b/agent/windows-setup-agent/SetupWizard.cs
new file mode 100644
index 0000000..327611c
--- /dev/null
+++ b/agent/windows-setup-agent/SetupWizard.cs
@@ -0,0 +1,574 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Windows.Forms;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Net.NetworkInformation;
+using System.Diagnostics;
+using System.Security.AccessControl;
+
+namespace Icinga
+{
+ public partial class SetupWizard : Form
+ {
+ private string _TrustedFile;
+ private string Icinga2User;
+
+ public SetupWizard()
+ {
+ InitializeComponent();
+
+ txtInstanceName.Text = Icinga2InstanceName;
+
+ Icinga2User = Program.Icinga2User;
+ txtUser.Text = Icinga2User;
+ }
+
+ private void Warning(string message)
+ {
+ MessageBox.Show(this, message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+
+ private string Icinga2InstanceName
+ {
+ get
+ {
+ IPGlobalProperties props = IPGlobalProperties.GetIPGlobalProperties();
+
+ string fqdn = props.HostName;
+
+ if (props.DomainName != "")
+ fqdn += "." + props.DomainName;
+
+ return fqdn.ToLower();
+ }
+ }
+
+ private bool GetMasterHostPort(out string host, out string port)
+ {
+ foreach (ListViewItem lvi in lvwEndpoints.Items) {
+ if (lvi.SubItems.Count > 1) {
+ host = lvi.SubItems[1].Text.Trim();
+ port = lvi.SubItems[2].Text.Trim();
+ return true;
+ }
+ }
+
+ host = null;
+ port = null;
+ return false;
+ }
+
+ private void EnableFeature(string feature)
+ {
+ FileStream fp = null;
+ try {
+ fp = File.Open(Program.Icinga2DataDir + String.Format("\\etc\\icinga2\\features-enabled\\{0}.conf", feature), FileMode.Create);
+ using (StreamWriter sw = new StreamWriter(fp, Encoding.ASCII)) {
+ fp = null;
+ sw.Write(String.Format("include \"../features-available/{0}.conf\"\n", feature));
+ }
+ } finally {
+ if (fp != null)
+ fp.Dispose();
+ }
+ }
+
+ private void SetRetrievalStatus(int pct)
+ {
+ if (InvokeRequired) {
+ Invoke((MethodInvoker)delegate { SetRetrievalStatus(pct); });
+ return;
+ }
+
+ prgRetrieveCertificate.Value = pct;
+ }
+
+ private void SetConfigureStatus(int pct, string message)
+ {
+ if (InvokeRequired) {
+ Invoke((MethodInvoker)delegate { SetConfigureStatus(pct, message); });
+ return;
+ }
+
+ prgConfig.Value = pct;
+ lblConfigStatus.Text = message;
+ }
+
+ private void ShowErrorText(string text)
+ {
+ if (InvokeRequired) {
+ Invoke((MethodInvoker)delegate { ShowErrorText(text); });
+ return;
+ }
+
+ txtError.Text = text;
+ tbcPages.SelectedTab = tabError;
+ }
+
+ private bool RunProcess(string filename, string arguments, out string output)
+ {
+ ProcessStartInfo psi = new ProcessStartInfo();
+ psi.FileName = filename;
+ psi.Arguments = arguments;
+ psi.CreateNoWindow = true;
+ psi.UseShellExecute = false;
+ psi.RedirectStandardOutput = true;
+ psi.RedirectStandardError = true;
+
+ String result = "";
+
+ using (Process proc = Process.Start(psi)) {
+ proc.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs args)
+ {
+ result += args.Data + "\r\n";
+ };
+ proc.OutputDataReceived += delegate (object sender, DataReceivedEventArgs args)
+ {
+ result += args.Data + "\r\n";
+ };
+ proc.BeginOutputReadLine();
+ proc.BeginErrorReadLine();
+ proc.WaitForExit();
+
+ output = result;
+
+ if (proc.ExitCode != 0)
+ return false;
+ }
+
+ return true;
+ }
+
+ private void VerifyCertificate(string host, string port)
+ {
+ SetRetrievalStatus(25);
+
+ string pathPrefix = Program.Icinga2DataDir + "\\etc\\icinga2\\pki\\" + txtInstanceName.Text;
+ string processArguments = "pki new-cert --cn \"" + txtInstanceName.Text + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\"";
+ string output;
+
+ if (!File.Exists(pathPrefix + ".crt")) {
+ if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
+ processArguments,
+ out output)) {
+ ShowErrorText("Running command 'icinga2.exe " + processArguments + "' produced the following output:\n" + output);
+ return;
+ }
+ }
+
+ SetRetrievalStatus(50);
+
+ _TrustedFile = Path.GetTempFileName();
+
+ processArguments = "pki save-cert --host \"" + host + "\" --port \"" + port + "\" --trustedcert \"" + _TrustedFile + "\"";
+ if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
+ processArguments,
+ out output)) {
+ ShowErrorText("Running command 'icinga2.exe " + processArguments + "' produced the following output:\n" + output);
+ return;
+ }
+
+ SetRetrievalStatus(100);
+ try {
+ X509Certificate2 cert = new X509Certificate2(_TrustedFile);
+ Invoke((MethodInvoker)delegate { ShowCertificatePrompt(cert); });
+ } catch (Exception e) {
+ ShowErrorText("Failed to receive certificate: " + e.Message);
+ }
+ }
+
+ private void ConfigureService()
+ {
+ SetConfigureStatus(0, "Updating configuration files...");
+
+ string output;
+
+ string args = "";
+
+ Invoke((MethodInvoker)delegate
+ {
+ string master_host, master_port;
+ GetMasterHostPort(out master_host, out master_port);
+
+ args += " --master_host " + master_host + "," + master_port;
+
+ foreach (ListViewItem lvi in lvwEndpoints.Items) {
+ args += " --endpoint " + lvi.SubItems[0].Text.Trim();
+
+ if (lvi.SubItems.Count > 1)
+ args += "," + lvi.SubItems[1].Text.Trim() + "," + lvi.SubItems[2].Text.Trim();
+ }
+ });
+
+ if (rdoListener.Checked)
+ args += " --listen ::," + txtListenerPort.Text.Trim();
+
+ if (chkAcceptConfig.Checked)
+ args += " --accept-config";
+
+ if (chkAcceptCommands.Checked)
+ args += " --accept-commands";
+
+ string ticket = txtTicket.Text.Trim();
+
+ if (ticket.Length > 0)
+ args += " --ticket \"" + ticket + "\"";
+
+ args += " --trustedcert \"" + _TrustedFile + "\"";
+ args += " --cn \"" + txtInstanceName.Text.Trim() + "\"";
+ args += " --zone \"" + txtInstanceName.Text.Trim() + "\"";
+
+ foreach (ListViewItem lvi in lvwGlobalZones.Items) {
+ args += " --global_zones " + lvi.SubItems[0].Text.Trim();
+ }
+
+ if (chkDisableConf.Checked)
+ args += " --disable-confd";
+
+ if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
+ "node setup" + args,
+ out output)) {
+ ShowErrorText("Running command 'icinga2.exe " + "node setup" + args + "' produced the following output:\n" + output);
+ return;
+ }
+
+ SetConfigureStatus(50, "Setting ACLs for the Icinga 2 directory...");
+
+ string serviceUser = txtUser.Text.Trim();
+
+ DirectoryInfo di = new DirectoryInfo(Program.Icinga2InstallDir);
+ DirectorySecurity ds = di.GetAccessControl();
+ FileSystemAccessRule rule = new FileSystemAccessRule(serviceUser,
+ FileSystemRights.Modify,
+ InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
+ try {
+ ds.AddAccessRule(rule);
+ di.SetAccessControl(ds);
+ } catch (System.Security.Principal.IdentityNotMappedException) {
+ ShowErrorText("Could not set ACLs for user \"" + serviceUser + "\". Identitiy is not mapped.\n");
+ return;
+ }
+
+ SetConfigureStatus(75, "Installing the Icinga 2 service...");
+
+ RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
+ "--scm-uninstall",
+ out output);
+
+ if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
+ "daemon --validate",
+ out output)) {
+ ShowErrorText("Running command 'icinga2.exe daemon --validate' produced the following output:\n" + output);
+ return;
+ }
+
+ if (!RunProcess(Program.Icinga2InstallDir + "\\sbin\\icinga2.exe",
+ "--scm-install --scm-user \"" + serviceUser + "\" daemon",
+ out output)) {
+ ShowErrorText("\nRunning command 'icinga2.exe --scm-install --scm-user \"" +
+ serviceUser + "\" daemon' produced the following output:\n" + output);
+ return;
+ }
+
+ SetConfigureStatus(100, "Finished.");
+
+ // Override the completed text
+ lblSetupCompleted.Text = "The Icinga Windows agent was set up successfully.";
+
+ // Add a note for the user for ticket-less signing
+ if (ticket.Length == 0) {
+ lblSetupCompleted.Text += "\n\nTicket was not specified. Please sign the certificate request on the Icinga 2 master node (requires v2.8+).";
+ }
+
+ FinishConfigure();
+ }
+
+ private void FinishConfigure()
+ {
+ if (InvokeRequired) {
+ Invoke((MethodInvoker)FinishConfigure);
+ return;
+ }
+
+ tbcPages.SelectedTab = tabFinish;
+ }
+
+ private void btnBack_Click(object sender, EventArgs e)
+ {
+ if (tbcPages.SelectedTab == tabError) {
+ tbcPages.SelectedIndex = 0;
+ return;
+ }
+
+ int offset = 1;
+
+ if (tbcPages.SelectedTab == tabVerifyCertificate)
+ offset++;
+
+ tbcPages.SelectedIndex -= offset;
+ }
+
+ private void btnNext_Click(object sender, EventArgs e)
+ {
+ if (tbcPages.SelectedTab == tabParameters) {
+ if (txtInstanceName.Text.Length == 0) {
+ Warning("Please enter an instance name.");
+ return;
+ }
+
+ if (lvwEndpoints.Items.Count == 0) {
+ Warning("You need to add at least one master/satellite endpoint.");
+ return;
+ }
+
+ string host, port;
+ if (!GetMasterHostPort(out host, out port)) {
+ Warning("Please enter a remote host and port for at least one of your endpoints.");
+ return;
+ }
+
+ if (rdoListener.Checked && (txtListenerPort.Text == "")) {
+ Warning("You need to specify a listener port.");
+ return;
+ }
+
+ if (txtUser.Text.Length == 0) {
+ Warning("Icinga 2 service user may not be empty.");
+ return;
+ }
+ }
+
+ if (tbcPages.SelectedTab == tabFinish || tbcPages.SelectedTab == tabError)
+ Application.Exit();
+
+ tbcPages.SelectedIndex++;
+ }
+
+ private void btnCancel_Click(object sender, EventArgs e)
+ {
+ Application.Exit();
+ }
+
+ private void tbcPages_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ Refresh();
+
+ btnBack.Enabled = (tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabError);
+ btnNext.Enabled = (tbcPages.SelectedTab == tabParameters || tbcPages.SelectedTab == tabVerifyCertificate || tbcPages.SelectedTab == tabFinish);
+
+ if (tbcPages.SelectedTab == tabFinish) {
+ btnNext.Text = "&Finish >";
+ btnCancel.Enabled = false;
+ }
+
+ if (tbcPages.SelectedTab == tabRetrieveCertificate) {
+ ListViewItem lvi = lvwEndpoints.Items[0];
+
+ string master_host, master_port;
+ GetMasterHostPort(out master_host, out master_port);
+
+ Thread thread = new Thread((ThreadStart)delegate { VerifyCertificate(master_host, master_port); });
+ thread.Start();
+ }
+
+ if (tbcPages.SelectedTab == tabConfigure) {
+ Thread thread = new Thread(ConfigureService);
+ thread.Start();
+ }
+ }
+
+ private void RadioListener_CheckedChanged(object sender, EventArgs e)
+ {
+ txtListenerPort.Enabled = rdoListener.Checked;
+ }
+
+ private void AddCertificateField(string name, string shortValue, string longValue = null)
+ {
+ ListViewItem lvi = new ListViewItem();
+ lvi.Text = name;
+ lvi.SubItems.Add(shortValue);
+ if (longValue == null)
+ longValue = shortValue;
+ lvi.Tag = longValue;
+ lvwX509Fields.Items.Add(lvi);
+ }
+
+ private string PadText(string input)
+ {
+ string output = "";
+
+ for (int i = 0; i < input.Length; i += 2) {
+ if (output != "")
+ output += " ";
+
+ int len = 2;
+ if (input.Length - i < 2)
+ len = input.Length - i;
+ output += input.Substring(i, len);
+ }
+
+ return output;
+ }
+
+ private void ShowCertificatePrompt(X509Certificate2 certificate)
+ {
+ txtX509Issuer.Text = certificate.Issuer;
+ txtX509Subject.Text = certificate.Subject;
+
+ lvwX509Fields.Items.Clear();
+
+ AddCertificateField("Version", "V" + certificate.Version.ToString());
+ AddCertificateField("Serial number", certificate.SerialNumber);
+ AddCertificateField("Signature algorithm", certificate.SignatureAlgorithm.FriendlyName);
+ AddCertificateField("Valid from", certificate.NotBefore.ToString());
+ AddCertificateField("Valid to", certificate.NotAfter.ToString());
+
+ string pkey = BitConverter.ToString(certificate.PublicKey.EncodedKeyValue.RawData).Replace("-", " ");
+ AddCertificateField("Public key", certificate.PublicKey.Oid.FriendlyName + " (" + certificate.PublicKey.Key.KeySize + " bits)", pkey);
+
+ string thumbprint = PadText(certificate.Thumbprint);
+ AddCertificateField("Thumbprint", thumbprint);
+
+ tbcPages.SelectedTab = tabVerifyCertificate;
+ }
+
+ private void btnAddEndpoint_Click(object sender, EventArgs e)
+ {
+ EndpointInputBox eib = new EndpointInputBox();
+
+ if (eib.ShowDialog(this) == DialogResult.Cancel)
+ return;
+
+ ListViewItem lvi = new ListViewItem();
+ lvi.Text = eib.txtInstanceName.Text;
+
+ if (eib.chkConnect.Checked) {
+ lvi.SubItems.Add(eib.txtHost.Text);
+ lvi.SubItems.Add(eib.txtPort.Text);
+ }
+
+ lvwEndpoints.Items.Add(lvi);
+ }
+
+ private void lvwEndpoints_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ btnRemoveEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
+ btnEditEndpoint.Enabled = lvwEndpoints.SelectedItems.Count > 0;
+ }
+
+ private void lvwX509Fields_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (lvwX509Fields.SelectedItems.Count == 0)
+ return;
+
+ ListViewItem lvi = lvwX509Fields.SelectedItems[0];
+
+ txtX509Field.Text = Convert.ToString(lvi.Tag);
+ }
+
+ private void btnRemoveEndpoint_Click(object sender, EventArgs e)
+ {
+ while (lvwEndpoints.SelectedItems.Count > 0) {
+ lvwEndpoints.Items.Remove(lvwEndpoints.SelectedItems[0]);
+ }
+ }
+
+ private void chkRunServiceAsThisUser_CheckedChanged(object sender, EventArgs e)
+ {
+ txtUser.Enabled = !txtUser.Enabled;
+ if (!txtUser.Enabled)
+ txtUser.Text = Icinga2User;
+ }
+
+ private void btnEditEndpoint_Click(object sender, EventArgs e)
+ {
+ ListViewItem lvi = lvwEndpoints.SelectedItems[0];
+ EndpointInputBox eib = new EndpointInputBox();
+
+ eib.Text = "Edit Endpoint";
+ eib.txtInstanceName.Text = lvi.SubItems[0].Text;
+
+ if (lvi.SubItems.Count >= 2) {
+ eib.txtHost.Text = lvi.SubItems[1].Text;
+ eib.txtPort.Text = lvi.SubItems[2].Text;
+ eib.chkConnect.Checked = true;
+ }
+
+ if (eib.ShowDialog(this) == DialogResult.Cancel)
+ return;
+
+ lvwEndpoints.Items.Remove(lvi);
+
+ ListViewItem lvi2 = new ListViewItem();
+ lvi2.Text = eib.txtInstanceName.Text;
+
+ if (eib.chkConnect.Checked) {
+ lvi2.SubItems.Add(eib.txtHost.Text);
+ lvi2.SubItems.Add(eib.txtPort.Text);
+ }
+
+ lvwEndpoints.Items.Add(lvi2);
+ }
+
+ private void btnAddGlobalZone_Click(object sender, EventArgs e)
+ {
+ GlobalZonesInputBox gzib = new GlobalZonesInputBox(lvwGlobalZones.Items);
+
+ if (gzib.ShowDialog(this) == DialogResult.Cancel)
+ return;
+
+ ListViewItem lvi = new ListViewItem();
+ lvi.Text = gzib.txtGlobalZoneName.Text;
+
+ lvwGlobalZones.Items.Add(lvi);
+ }
+
+ private void btnRemoveGlobalZone_Click(object sender, EventArgs e)
+ {
+ while (lvwGlobalZones.SelectedItems.Count > 0) {
+ lvwGlobalZones.Items.Remove(lvwGlobalZones.SelectedItems[0]);
+ }
+ }
+
+ private void lvwGlobalZones_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ btnEditGlobalZone.Enabled = lvwGlobalZones.SelectedItems.Count > 0;
+ btnRemoveGlobalZone.Enabled = lvwGlobalZones.SelectedItems.Count > 0;
+ }
+
+ private void btnEditGlobalZone_Click(object sender, EventArgs e)
+ {
+ ListViewItem lvi = lvwGlobalZones.SelectedItems[0];
+ GlobalZonesInputBox gzib = new GlobalZonesInputBox(lvwGlobalZones.Items);
+
+ gzib.Text = "Edit Global Zone";
+ gzib.txtGlobalZoneName.Text = lvi.SubItems[0].Text;
+
+ if (gzib.ShowDialog(this) == DialogResult.Cancel)
+ return;
+
+ lvwGlobalZones.Items.Remove(lvi);
+
+ ListViewItem lvi2 = new ListViewItem();
+ lvi2.Text = gzib.txtGlobalZoneName.Text;
+
+ lvwGlobalZones.Items.Add(lvi2);
+ }
+
+ private void SetupWizard_Load(object sender, EventArgs e)
+ {
+ this.MinimumSize = this.Size;
+ this.MaximumSize = this.Size;
+ }
+
+ private void linkLabelDocs_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ linkLabelDocs.LinkVisited = true;
+
+ Process.Start("https://icinga.com/docs/icinga2/latest/");
+ }
+ }
+}
+