mirror of https://github.com/Icinga/icinga2.git
585 lines
16 KiB
C#
585 lines
16 KiB
C#
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;
|
|
}
|
|
}
|
|
|
|
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 + "\" --key \"" + pathPrefix + ".key\" --cert \"" + pathPrefix + ".crt\" --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;
|
|
}
|
|
|
|
if (chkInstallNSCP.Checked) {
|
|
SetConfigureStatus(85, "Waiting for NSClient++ installation to complete...");
|
|
|
|
Process proc = new Process();
|
|
proc.StartInfo.FileName = "msiexec.exe";
|
|
proc.StartInfo.Arguments = "/i \"" + Program.Icinga2InstallDir + "\\sbin\\NSCP.msi\"";
|
|
proc.Start();
|
|
proc.WaitForExit();
|
|
}
|
|
|
|
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/");
|
|
}
|
|
}
|
|
}
|
|
|