feat: Add CrowdSec Console enrollment to setup wizard (v0.6.0-r28)
- Add console_status, console_enroll, console_disable RPCD methods - Insert Console enrollment as Step 2 in the 7-step wizard - Add API declarations and ACL permissions for console operations - Enable share_manual_decisions, share_tainted, share_context by default on enrollment Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4078b4d7a4
commit
e5b6d1dd87
@ -197,6 +197,26 @@ var callRepairLapi = rpc.declare({
|
|||||||
expect: { }
|
expect: { }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Console Methods
|
||||||
|
var callConsoleStatus = rpc.declare({
|
||||||
|
object: 'luci.crowdsec-dashboard',
|
||||||
|
method: 'console_status',
|
||||||
|
expect: { }
|
||||||
|
});
|
||||||
|
|
||||||
|
var callConsoleEnroll = rpc.declare({
|
||||||
|
object: 'luci.crowdsec-dashboard',
|
||||||
|
method: 'console_enroll',
|
||||||
|
params: ['key', 'name'],
|
||||||
|
expect: { }
|
||||||
|
});
|
||||||
|
|
||||||
|
var callConsoleDisable = rpc.declare({
|
||||||
|
object: 'luci.crowdsec-dashboard',
|
||||||
|
method: 'console_disable',
|
||||||
|
expect: { }
|
||||||
|
});
|
||||||
|
|
||||||
function formatDuration(seconds) {
|
function formatDuration(seconds) {
|
||||||
if (!seconds) return 'N/A';
|
if (!seconds) return 'N/A';
|
||||||
if (seconds < 60) return seconds + 's';
|
if (seconds < 60) return seconds + 's';
|
||||||
@ -318,6 +338,11 @@ return baseclass.extend({
|
|||||||
getWizardState: callWizardState,
|
getWizardState: callWizardState,
|
||||||
repairLapi: callRepairLapi,
|
repairLapi: callRepairLapi,
|
||||||
|
|
||||||
|
// Console Methods
|
||||||
|
getConsoleStatus: callConsoleStatus,
|
||||||
|
consoleEnroll: callConsoleEnroll,
|
||||||
|
consoleDisable: callConsoleDisable,
|
||||||
|
|
||||||
formatDuration: formatDuration,
|
formatDuration: formatDuration,
|
||||||
formatDate: formatDate,
|
formatDate: formatDate,
|
||||||
formatRelativeTime: formatRelativeTime,
|
formatRelativeTime: formatRelativeTime,
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
return view.extend({
|
return view.extend({
|
||||||
wizardData: {
|
wizardData: {
|
||||||
currentStep: 1,
|
currentStep: 1,
|
||||||
totalSteps: 6,
|
totalSteps: 7,
|
||||||
|
|
||||||
// Step 1 data
|
// Step 1 data
|
||||||
crowdsecRunning: false,
|
crowdsecRunning: false,
|
||||||
@ -18,23 +18,29 @@ return view.extend({
|
|||||||
lapiRepairing: false,
|
lapiRepairing: false,
|
||||||
lapiRepairAttempted: false,
|
lapiRepairAttempted: false,
|
||||||
|
|
||||||
// Step 2 data
|
// Step 2 data (Console Enrollment)
|
||||||
|
consoleEnrolled: false,
|
||||||
|
enrolling: false,
|
||||||
|
enrollmentKey: '',
|
||||||
|
machineName: '',
|
||||||
|
|
||||||
|
// Step 3 data (Hub Update)
|
||||||
hubUpdating: false,
|
hubUpdating: false,
|
||||||
hubUpdated: false,
|
hubUpdated: false,
|
||||||
|
|
||||||
// Step 3 data
|
// Step 4 data (Collections)
|
||||||
collections: [],
|
collections: [],
|
||||||
installing: false,
|
installing: false,
|
||||||
installed: false,
|
installed: false,
|
||||||
installStatus: '',
|
installStatus: '',
|
||||||
installedCount: 0,
|
installedCount: 0,
|
||||||
|
|
||||||
// Step 4 data
|
// Step 5 data (Bouncer)
|
||||||
configuring: false,
|
configuring: false,
|
||||||
bouncerConfigured: false,
|
bouncerConfigured: false,
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
|
|
||||||
// Step 5 data
|
// Step 6 data (Services)
|
||||||
starting: false,
|
starting: false,
|
||||||
enabling: false,
|
enabling: false,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -42,7 +48,7 @@ return view.extend({
|
|||||||
nftablesActive: false,
|
nftablesActive: false,
|
||||||
lapiConnected: false,
|
lapiConnected: false,
|
||||||
|
|
||||||
// Step 6 data
|
// Step 7 data (Complete)
|
||||||
blockedIPs: 0,
|
blockedIPs: 0,
|
||||||
activeDecisions: 0
|
activeDecisions: 0
|
||||||
},
|
},
|
||||||
@ -50,14 +56,17 @@ return view.extend({
|
|||||||
load: function() {
|
load: function() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
API.getStatus(),
|
API.getStatus(),
|
||||||
API.checkWizardNeeded()
|
API.checkWizardNeeded(),
|
||||||
|
API.getConsoleStatus()
|
||||||
]).then(L.bind(function(results) {
|
]).then(L.bind(function(results) {
|
||||||
var status = results[0];
|
var status = results[0];
|
||||||
var wizardNeeded = results[1];
|
var wizardNeeded = results[1];
|
||||||
|
var consoleStatus = results[2];
|
||||||
|
|
||||||
// Update wizard data from status
|
// Update wizard data from status
|
||||||
this.wizardData.crowdsecRunning = status && status.crowdsec === 'running';
|
this.wizardData.crowdsecRunning = status && status.crowdsec === 'running';
|
||||||
this.wizardData.lapiAvailable = status && status.lapi_status === 'available';
|
this.wizardData.lapiAvailable = status && status.lapi_status === 'available';
|
||||||
|
this.wizardData.consoleEnrolled = consoleStatus && consoleStatus.enrolled;
|
||||||
|
|
||||||
// Auto-repair LAPI if CrowdSec is running but LAPI is not available
|
// Auto-repair LAPI if CrowdSec is running but LAPI is not available
|
||||||
if (this.wizardData.crowdsecRunning && !this.wizardData.lapiAvailable && !this.wizardData.lapiRepairAttempted) {
|
if (this.wizardData.crowdsecRunning && !this.wizardData.lapiAvailable && !this.wizardData.lapiRepairAttempted) {
|
||||||
@ -78,6 +87,7 @@ return view.extend({
|
|||||||
return {
|
return {
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
wizardNeeded: wizardNeeded,
|
wizardNeeded: wizardNeeded,
|
||||||
|
consoleStatus: consoleStatus,
|
||||||
repaired: true
|
repaired: true
|
||||||
};
|
};
|
||||||
}, this));
|
}, this));
|
||||||
@ -86,6 +96,7 @@ return view.extend({
|
|||||||
return {
|
return {
|
||||||
status: status,
|
status: status,
|
||||||
wizardNeeded: wizardNeeded,
|
wizardNeeded: wizardNeeded,
|
||||||
|
consoleStatus: consoleStatus,
|
||||||
repairFailed: true
|
repairFailed: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -94,12 +105,16 @@ return view.extend({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
status: status,
|
status: status,
|
||||||
wizardNeeded: wizardNeeded
|
wizardNeeded: wizardNeeded,
|
||||||
|
consoleStatus: consoleStatus
|
||||||
};
|
};
|
||||||
}, this));
|
}, this));
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function(data) {
|
render: function(data) {
|
||||||
|
// Initialize theme
|
||||||
|
Theme.init();
|
||||||
|
|
||||||
// Load wizard CSS
|
// Load wizard CSS
|
||||||
var head = document.head || document.getElementsByTagName('head')[0];
|
var head = document.head || document.getElementsByTagName('head')[0];
|
||||||
var cssLink = E('link', {
|
var cssLink = E('link', {
|
||||||
@ -109,6 +124,14 @@ return view.extend({
|
|||||||
});
|
});
|
||||||
head.appendChild(cssLink);
|
head.appendChild(cssLink);
|
||||||
|
|
||||||
|
// Load SecuBox theme CSS
|
||||||
|
var themeLink = E('link', {
|
||||||
|
'rel': 'stylesheet',
|
||||||
|
'type': 'text/css',
|
||||||
|
'href': L.resource('secubox-theme/secubox-theme.css')
|
||||||
|
});
|
||||||
|
head.appendChild(themeLink);
|
||||||
|
|
||||||
var container = E('div', { 'class': 'wizard-container' });
|
var container = E('div', { 'class': 'wizard-container' });
|
||||||
|
|
||||||
// Create stepper
|
// Create stepper
|
||||||
@ -123,11 +146,12 @@ return view.extend({
|
|||||||
createStepper: function() {
|
createStepper: function() {
|
||||||
var steps = [
|
var steps = [
|
||||||
{ number: 1, title: _('Welcome') },
|
{ number: 1, title: _('Welcome') },
|
||||||
{ number: 2, title: _('Update Hub') },
|
{ number: 2, title: _('Console') },
|
||||||
{ number: 3, title: _('Install Packs') },
|
{ number: 3, title: _('Update Hub') },
|
||||||
{ number: 4, title: _('Configure Bouncer') },
|
{ number: 4, title: _('Install Packs') },
|
||||||
{ number: 5, title: _('Enable Services') },
|
{ number: 5, title: _('Configure Bouncer') },
|
||||||
{ number: 6, title: _('Complete') }
|
{ number: 6, title: _('Enable Services') },
|
||||||
|
{ number: 7, title: _('Complete') }
|
||||||
];
|
];
|
||||||
|
|
||||||
var stepper = E('div', { 'class': 'wizard-stepper' });
|
var stepper = E('div', { 'class': 'wizard-stepper' });
|
||||||
@ -158,15 +182,17 @@ return view.extend({
|
|||||||
case 1:
|
case 1:
|
||||||
return this.renderStep1(data);
|
return this.renderStep1(data);
|
||||||
case 2:
|
case 2:
|
||||||
return this.renderStep2(data);
|
return this.renderStep2Console(data);
|
||||||
case 3:
|
case 3:
|
||||||
return this.renderStep3(data);
|
return this.renderStep3Hub(data);
|
||||||
case 4:
|
case 4:
|
||||||
return this.renderStep4(data);
|
return this.renderStep4Collections(data);
|
||||||
case 5:
|
case 5:
|
||||||
return this.renderStep5(data);
|
return this.renderStep5Bouncer(data);
|
||||||
case 6:
|
case 6:
|
||||||
return this.renderStep6(data);
|
return this.renderStep6Services(data);
|
||||||
|
case 7:
|
||||||
|
return this.renderStep7Complete(data);
|
||||||
default:
|
default:
|
||||||
return E('div', {}, _('Invalid step'));
|
return E('div', {}, _('Invalid step'));
|
||||||
}
|
}
|
||||||
@ -225,18 +251,18 @@ return view.extend({
|
|||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-action',
|
'class': 'cbi-button cbi-button-action',
|
||||||
'click': L.bind(this.handleManualRepair, this)
|
'click': L.bind(this.handleManualRepair, this)
|
||||||
}, _('🔧 Retry Repair'))
|
}, _('Retry Repair'))
|
||||||
]) : E([]),
|
]) : E([]),
|
||||||
|
|
||||||
// Info box
|
// Info box
|
||||||
E('div', { 'class': 'info-box' }, [
|
E('div', { 'class': 'info-box' }, [
|
||||||
E('h4', {}, _('What will be configured:')),
|
E('h4', {}, _('What will be configured:')),
|
||||||
E('ul', {}, [
|
E('ul', {}, [
|
||||||
|
E('li', {}, _('Enroll in CrowdSec Console for community blocklists')),
|
||||||
E('li', {}, _('Update CrowdSec hub with latest collections')),
|
E('li', {}, _('Update CrowdSec hub with latest collections')),
|
||||||
E('li', {}, _('Install essential security scenarios')),
|
E('li', {}, _('Install essential security scenarios')),
|
||||||
E('li', {}, _('Register and configure firewall bouncer')),
|
E('li', {}, _('Register and configure firewall bouncer')),
|
||||||
E('li', {}, _('Enable automatic IP blocking via nftables')),
|
E('li', {}, _('Enable automatic IP blocking via nftables'))
|
||||||
E('li', {}, _('Start all services'))
|
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -257,12 +283,107 @@ return view.extend({
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.goToStep(2);
|
this.goToStep(2);
|
||||||
}, this)
|
}, this)
|
||||||
}, _('Next →'))
|
}, _('Next'))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
renderStep2: function(data) {
|
renderStep2Console: function(data) {
|
||||||
|
var consoleStatus = data && data.consoleStatus ? data.consoleStatus : {};
|
||||||
|
var enrolled = consoleStatus.enrolled || this.wizardData.consoleEnrolled;
|
||||||
|
|
||||||
|
return E('div', { 'class': 'wizard-step' }, [
|
||||||
|
E('h2', {}, _('CrowdSec Console Enrollment')),
|
||||||
|
E('p', {}, _('Connect to CrowdSec Console to receive community blocklists and monitor your security.')),
|
||||||
|
|
||||||
|
// Benefits
|
||||||
|
E('div', { 'class': 'info-box', 'style': 'margin-bottom: 24px;' }, [
|
||||||
|
E('h4', {}, _('Benefits of enrolling:')),
|
||||||
|
E('ul', {}, [
|
||||||
|
E('li', {}, _('Access to community-curated blocklists')),
|
||||||
|
E('li', {}, _('Real-time threat intelligence sharing')),
|
||||||
|
E('li', {}, _('Centralized monitoring dashboard')),
|
||||||
|
E('li', {}, _('Free tier available'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Enrollment status
|
||||||
|
enrolled ?
|
||||||
|
E('div', { 'class': 'success-message', 'style': 'margin: 24px 0; padding: 16px; background: rgba(34, 197, 94, 0.15); border-radius: 8px;' }, [
|
||||||
|
E('span', { 'class': 'check-icon success', 'style': 'font-size: 24px; margin-right: 12px;' }, '✓'),
|
||||||
|
E('div', { 'style': 'display: inline-block;' }, [
|
||||||
|
E('strong', { 'style': 'display: block; color: #16a34a;' }, _('Already enrolled!')),
|
||||||
|
E('span', { 'style': 'color: #15803d; font-size: 0.9em;' },
|
||||||
|
_('Your instance is connected to CrowdSec Console'))
|
||||||
|
])
|
||||||
|
]) :
|
||||||
|
E('div', { 'class': 'enrollment-form', 'style': 'margin: 24px 0;' }, [
|
||||||
|
// Enrollment key input
|
||||||
|
E('div', { 'class': 'form-group', 'style': 'margin-bottom: 16px;' }, [
|
||||||
|
E('label', { 'style': 'display: block; margin-bottom: 8px; font-weight: 500;' },
|
||||||
|
_('Enrollment Key')),
|
||||||
|
E('input', {
|
||||||
|
'type': 'text',
|
||||||
|
'id': 'console-enrollment-key',
|
||||||
|
'class': 'cbi-input-text',
|
||||||
|
'placeholder': _('Paste your enrollment key from console.crowdsec.net'),
|
||||||
|
'style': 'width: 100%; padding: 12px; font-family: monospace;'
|
||||||
|
}),
|
||||||
|
E('p', { 'style': 'margin-top: 8px; color: var(--cyber-text-secondary, #666); font-size: 0.9em;' }, [
|
||||||
|
_('Get your key from '),
|
||||||
|
E('a', {
|
||||||
|
'href': 'https://app.crowdsec.net/security-engines',
|
||||||
|
'target': '_blank',
|
||||||
|
'style': 'color: var(--cyber-accent-primary, #667eea);'
|
||||||
|
}, 'app.crowdsec.net')
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Machine name (optional)
|
||||||
|
E('div', { 'class': 'form-group', 'style': 'margin-bottom: 16px;' }, [
|
||||||
|
E('label', { 'style': 'display: block; margin-bottom: 8px; font-weight: 500;' },
|
||||||
|
_('Machine Name (optional)')),
|
||||||
|
E('input', {
|
||||||
|
'type': 'text',
|
||||||
|
'id': 'console-machine-name',
|
||||||
|
'class': 'cbi-input-text',
|
||||||
|
'placeholder': _('e.g., secubox-router'),
|
||||||
|
'style': 'width: 100%; padding: 12px;'
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Enrolling status
|
||||||
|
this.wizardData.enrolling ?
|
||||||
|
E('div', { 'style': 'text-align: center; padding: 16px;' }, [
|
||||||
|
E('div', { 'class': 'spinning' }),
|
||||||
|
E('p', {}, _('Enrolling...'))
|
||||||
|
]) : E([]),
|
||||||
|
|
||||||
|
// Enroll button
|
||||||
|
E('button', {
|
||||||
|
'class': 'cbi-button cbi-button-action',
|
||||||
|
'style': 'width: 100%; padding: 12px; font-size: 1em;',
|
||||||
|
'disabled': this.wizardData.enrolling ? true : null,
|
||||||
|
'click': L.bind(this.handleConsoleEnroll, this)
|
||||||
|
}, _('Enroll in CrowdSec Console'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
E('div', { 'class': 'wizard-nav' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'cbi-button',
|
||||||
|
'click': L.bind(this.goToStep, this, 1)
|
||||||
|
}, _('Back')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'cbi-button',
|
||||||
|
'click': L.bind(this.goToStep, this, 3),
|
||||||
|
'disabled': this.wizardData.enrolling ? true : null
|
||||||
|
}, enrolled ? _('Next') : _('Skip'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderStep3Hub: function(data) {
|
||||||
return E('div', { 'class': 'wizard-step' }, [
|
return E('div', { 'class': 'wizard-step' }, [
|
||||||
E('h2', {}, _('Update CrowdSec Hub')),
|
E('h2', {}, _('Update CrowdSec Hub')),
|
||||||
E('p', {}, _('Fetching the latest security collections from CrowdSec hub...')),
|
E('p', {}, _('Fetching the latest security collections from CrowdSec hub...')),
|
||||||
@ -282,13 +403,13 @@ return view.extend({
|
|||||||
E('div', { 'class': 'wizard-nav' }, [
|
E('div', { 'class': 'wizard-nav' }, [
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button',
|
'class': 'cbi-button',
|
||||||
'click': L.bind(this.goToStep, this, 1)
|
'click': L.bind(this.goToStep, this, 2)
|
||||||
}, _('← Back')),
|
}, _('Back')),
|
||||||
this.wizardData.hubUpdated ?
|
this.wizardData.hubUpdated ?
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-positive',
|
'class': 'cbi-button cbi-button-positive',
|
||||||
'click': L.bind(this.goToStep, this, 3)
|
'click': L.bind(this.goToStep, this, 4)
|
||||||
}, _('Next →')) :
|
}, _('Next')) :
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-action',
|
'class': 'cbi-button cbi-button-action',
|
||||||
'click': L.bind(this.handleUpdateHub, this)
|
'click': L.bind(this.handleUpdateHub, this)
|
||||||
@ -297,7 +418,7 @@ return view.extend({
|
|||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
renderStep3: function(data) {
|
renderStep4Collections: function(data) {
|
||||||
var recommendedCollections = [
|
var recommendedCollections = [
|
||||||
{ name: 'crowdsecurity/linux', description: 'Base Linux scenarios', preselected: true },
|
{ name: 'crowdsecurity/linux', description: 'Base Linux scenarios', preselected: true },
|
||||||
{ name: 'crowdsecurity/ssh-bf', description: 'SSH brute force protection', preselected: true },
|
{ name: 'crowdsecurity/ssh-bf', description: 'SSH brute force protection', preselected: true },
|
||||||
@ -356,24 +477,24 @@ return view.extend({
|
|||||||
E('div', { 'class': 'wizard-nav' }, [
|
E('div', { 'class': 'wizard-nav' }, [
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button',
|
'class': 'cbi-button',
|
||||||
'click': L.bind(this.goToStep, this, 2),
|
'click': L.bind(this.goToStep, this, 3),
|
||||||
'disabled': this.wizardData.installing ? true : null
|
'disabled': this.wizardData.installing ? true : null
|
||||||
}, _('← Back')),
|
}, _('Back')),
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button',
|
'class': 'cbi-button',
|
||||||
'click': L.bind(this.goToStep, this, 4),
|
'click': L.bind(this.goToStep, this, 5),
|
||||||
'disabled': this.wizardData.installing ? true : null
|
'disabled': this.wizardData.installing ? true : null
|
||||||
}, _('Skip')),
|
}, _('Skip')),
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-positive',
|
'class': 'cbi-button cbi-button-positive',
|
||||||
'click': L.bind(this.handleInstallCollections, this),
|
'click': L.bind(this.handleInstallCollections, this),
|
||||||
'disabled': (this.wizardData.installing || this.wizardData.installed) ? true : null
|
'disabled': (this.wizardData.installing || this.wizardData.installed) ? true : null
|
||||||
}, this.wizardData.installed ? _('Installed ✓') : _('Install Selected'))
|
}, this.wizardData.installed ? _('Installed') : _('Install Selected'))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
renderStep4: function(data) {
|
renderStep5Bouncer: function(data) {
|
||||||
return E('div', { 'class': 'wizard-step' }, [
|
return E('div', { 'class': 'wizard-step' }, [
|
||||||
E('h2', {}, _('Configure Firewall Bouncer')),
|
E('h2', {}, _('Configure Firewall Bouncer')),
|
||||||
E('p', {}, _('The firewall bouncer will automatically block malicious IPs using nftables.')),
|
E('p', {}, _('The firewall bouncer will automatically block malicious IPs using nftables.')),
|
||||||
@ -465,24 +586,24 @@ return view.extend({
|
|||||||
E('div', { 'class': 'wizard-nav' }, [
|
E('div', { 'class': 'wizard-nav' }, [
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button',
|
'class': 'cbi-button',
|
||||||
'click': L.bind(this.goToStep, this, 3),
|
'click': L.bind(this.goToStep, this, 4),
|
||||||
'disabled': this.wizardData.configuring ? true : null
|
'disabled': this.wizardData.configuring ? true : null
|
||||||
}, _('← Back')),
|
}, _('Back')),
|
||||||
this.wizardData.bouncerConfigured ?
|
this.wizardData.bouncerConfigured ?
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-positive',
|
'class': 'cbi-button cbi-button-positive',
|
||||||
'click': L.bind(this.goToStep, this, 5)
|
'click': L.bind(this.goToStep, this, 6)
|
||||||
}, _('Next →')) :
|
}, _('Next')) :
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-action',
|
'class': 'cbi-button cbi-button-action',
|
||||||
'click': L.bind(function(ev) { console.log('[Wizard] Configure Bouncer button clicked!'); ev.preventDefault(); ev.stopPropagation(); this.handleConfigureBouncer(); }, this),
|
'click': L.bind(this.handleConfigureBouncer, this),
|
||||||
'disabled': this.wizardData.configuring ? true : null
|
'disabled': this.wizardData.configuring ? true : null
|
||||||
}, _('Configure Bouncer'))
|
}, _('Configure Bouncer'))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
renderStep5: function(data) {
|
renderStep6Services: function(data) {
|
||||||
return E('div', { 'class': 'wizard-step' }, [
|
return E('div', { 'class': 'wizard-step' }, [
|
||||||
E('h2', {}, _('Enable & Start Services')),
|
E('h2', {}, _('Enable & Start Services')),
|
||||||
E('p', {}, _('Starting the firewall bouncer service and verifying operation...')),
|
E('p', {}, _('Starting the firewall bouncer service and verifying operation...')),
|
||||||
@ -492,22 +613,22 @@ return view.extend({
|
|||||||
E('div', { 'class': 'status-item' }, [
|
E('div', { 'class': 'status-item' }, [
|
||||||
E('span', { 'class': 'status-label' }, _('Enable at boot:')),
|
E('span', { 'class': 'status-label' }, _('Enable at boot:')),
|
||||||
E('span', { 'class': 'status-value' + (this.wizardData.enabled ? ' success' : '') },
|
E('span', { 'class': 'status-value' + (this.wizardData.enabled ? ' success' : '') },
|
||||||
this.wizardData.enabled ? _('Enabled ✓') : this.wizardData.enabling ? _('Enabling...') : _('Not enabled'))
|
this.wizardData.enabled ? _('Enabled') : this.wizardData.enabling ? _('Enabling...') : _('Not enabled'))
|
||||||
]),
|
]),
|
||||||
E('div', { 'class': 'status-item' }, [
|
E('div', { 'class': 'status-item' }, [
|
||||||
E('span', { 'class': 'status-label' }, _('Service status:')),
|
E('span', { 'class': 'status-label' }, _('Service status:')),
|
||||||
E('span', { 'class': 'status-value' + (this.wizardData.running ? ' success' : '') },
|
E('span', { 'class': 'status-value' + (this.wizardData.running ? ' success' : '') },
|
||||||
this.wizardData.running ? _('Running ✓') : this.wizardData.starting ? _('Starting...') : _('Stopped'))
|
this.wizardData.running ? _('Running') : this.wizardData.starting ? _('Starting...') : _('Stopped'))
|
||||||
]),
|
]),
|
||||||
E('div', { 'class': 'status-item' }, [
|
E('div', { 'class': 'status-item' }, [
|
||||||
E('span', { 'class': 'status-label' }, _('nftables rules:')),
|
E('span', { 'class': 'status-label' }, _('nftables rules:')),
|
||||||
E('span', { 'class': 'status-value' + (this.wizardData.nftablesActive ? ' success' : '') },
|
E('span', { 'class': 'status-value' + (this.wizardData.nftablesActive ? ' success' : '') },
|
||||||
this.wizardData.nftablesActive ? _('Loaded ✓') : _('Not loaded'))
|
this.wizardData.nftablesActive ? _('Loaded') : _('Not loaded'))
|
||||||
]),
|
]),
|
||||||
E('div', { 'class': 'status-item' }, [
|
E('div', { 'class': 'status-item' }, [
|
||||||
E('span', { 'class': 'status-label' }, _('LAPI connection:')),
|
E('span', { 'class': 'status-label' }, _('LAPI connection:')),
|
||||||
E('span', { 'class': 'status-value' + (this.wizardData.lapiConnected ? ' success' : '') },
|
E('span', { 'class': 'status-value' + (this.wizardData.lapiConnected ? ' success' : '') },
|
||||||
this.wizardData.lapiConnected ? _('Connected ✓') : _('Not connected'))
|
this.wizardData.lapiConnected ? _('Connected') : _('Not connected'))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -515,24 +636,24 @@ return view.extend({
|
|||||||
E('div', { 'class': 'wizard-nav' }, [
|
E('div', { 'class': 'wizard-nav' }, [
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button',
|
'class': 'cbi-button',
|
||||||
'click': L.bind(this.goToStep, this, 4),
|
'click': L.bind(this.goToStep, this, 5),
|
||||||
'disabled': this.wizardData.starting ? true : null
|
'disabled': this.wizardData.starting ? true : null
|
||||||
}, _('← Back')),
|
}, _('Back')),
|
||||||
(this.wizardData.enabled && this.wizardData.running && this.wizardData.nftablesActive && this.wizardData.lapiConnected) ?
|
(this.wizardData.enabled && this.wizardData.running && this.wizardData.nftablesActive && this.wizardData.lapiConnected) ?
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-positive',
|
'class': 'cbi-button cbi-button-positive',
|
||||||
'click': L.bind(this.goToStep, this, 6)
|
'click': L.bind(this.goToStep, this, 7)
|
||||||
}, _('Next →')) :
|
}, _('Next')) :
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'cbi-button cbi-button-action',
|
'class': 'cbi-button cbi-button-action',
|
||||||
'click': L.bind(function(ev) { console.log('[Wizard] Start Services button clicked!'); ev.preventDefault(); ev.stopPropagation(); this.handleStartServices(); }, this),
|
'click': L.bind(this.handleStartServices, this),
|
||||||
'disabled': this.wizardData.starting ? true : null
|
'disabled': this.wizardData.starting ? true : null
|
||||||
}, _('Start Services'))
|
}, _('Start Services'))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|
||||||
renderStep6: function(data) {
|
renderStep7Complete: function(data) {
|
||||||
return E('div', { 'class': 'wizard-step wizard-complete' }, [
|
return E('div', { 'class': 'wizard-step wizard-complete' }, [
|
||||||
E('div', { 'class': 'success-hero' }, [
|
E('div', { 'class': 'success-hero' }, [
|
||||||
E('div', { 'class': 'success-icon' }, '🎉'),
|
E('div', { 'class': 'success-icon' }, '🎉'),
|
||||||
@ -550,6 +671,14 @@ return view.extend({
|
|||||||
E('div', { 'class': 'summary-desc' }, _('Running and monitoring'))
|
E('div', { 'class': 'summary-desc' }, _('Running and monitoring'))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
this.wizardData.consoleEnrolled ?
|
||||||
|
E('div', { 'class': 'summary-item' }, [
|
||||||
|
E('span', { 'class': 'check-icon success' }, '✓'),
|
||||||
|
E('div', {}, [
|
||||||
|
E('strong', {}, _('Console Enrolled')),
|
||||||
|
E('div', { 'class': 'summary-desc' }, _('Receiving community blocklists'))
|
||||||
|
])
|
||||||
|
]) : E([]),
|
||||||
E('div', { 'class': 'summary-item' }, [
|
E('div', { 'class': 'summary-item' }, [
|
||||||
E('span', { 'class': 'check-icon success' }, '✓'),
|
E('span', { 'class': 'check-icon success' }, '✓'),
|
||||||
E('div', {}, [
|
E('div', {}, [
|
||||||
@ -607,7 +736,12 @@ return view.extend({
|
|||||||
E('li', {}, _('View real-time decisions in the Decisions tab')),
|
E('li', {}, _('View real-time decisions in the Decisions tab')),
|
||||||
E('li', {}, _('Monitor alerts in the Alerts tab')),
|
E('li', {}, _('Monitor alerts in the Alerts tab')),
|
||||||
E('li', {}, _('Check blocked IPs in the Bouncers tab')),
|
E('li', {}, _('Check blocked IPs in the Bouncers tab')),
|
||||||
E('li', {}, _('Review metrics in the Metrics tab'))
|
this.wizardData.consoleEnrolled ?
|
||||||
|
E('li', {}, [
|
||||||
|
_('Monitor from '),
|
||||||
|
E('a', { 'href': 'https://app.crowdsec.net', 'target': '_blank' }, 'CrowdSec Console')
|
||||||
|
]) :
|
||||||
|
E('li', {}, _('Consider enrolling in CrowdSec Console for blocklists'))
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -619,7 +753,7 @@ return view.extend({
|
|||||||
'click': function() {
|
'click': function() {
|
||||||
window.location.href = L.url('admin', 'secubox', 'security', 'crowdsec', 'overview');
|
window.location.href = L.url('admin', 'secubox', 'security', 'crowdsec', 'overview');
|
||||||
}
|
}
|
||||||
}, _('Go to Dashboard →'))
|
}, _('Go to Dashboard'))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
@ -644,6 +778,42 @@ return view.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleConsoleEnroll: function() {
|
||||||
|
var keyInput = document.getElementById('console-enrollment-key');
|
||||||
|
var nameInput = document.getElementById('console-machine-name');
|
||||||
|
var key = keyInput ? keyInput.value.trim() : '';
|
||||||
|
var name = nameInput ? nameInput.value.trim() : '';
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
ui.addNotification(null, E('p', _('Please enter an enrollment key')), 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Wizard] Enrolling with key:', key.substring(0, 10) + '...');
|
||||||
|
this.wizardData.enrolling = true;
|
||||||
|
this.refreshView();
|
||||||
|
|
||||||
|
return API.consoleEnroll(key, name).then(L.bind(function(result) {
|
||||||
|
console.log('[Wizard] Enrollment result:', result);
|
||||||
|
this.wizardData.enrolling = false;
|
||||||
|
|
||||||
|
if (result && result.success) {
|
||||||
|
this.wizardData.consoleEnrolled = true;
|
||||||
|
ui.addNotification(null, E('p', _('Successfully enrolled in CrowdSec Console!')), 'success');
|
||||||
|
// Auto-advance after 2 seconds
|
||||||
|
setTimeout(L.bind(function() { this.goToStep(3); }, this), 2000);
|
||||||
|
} else {
|
||||||
|
ui.addNotification(null, E('p', _('Enrollment failed: ') + (result.error || result.output || 'Unknown error')), 'error');
|
||||||
|
}
|
||||||
|
this.refreshView();
|
||||||
|
}, this)).catch(L.bind(function(err) {
|
||||||
|
console.error('[Wizard] Enrollment error:', err);
|
||||||
|
this.wizardData.enrolling = false;
|
||||||
|
ui.addNotification(null, E('p', _('Enrollment failed: ') + err.message), 'error');
|
||||||
|
this.refreshView();
|
||||||
|
}, this));
|
||||||
|
},
|
||||||
|
|
||||||
handleUpdateHub: function() {
|
handleUpdateHub: function() {
|
||||||
console.log('[Wizard] handleUpdateHub called');
|
console.log('[Wizard] handleUpdateHub called');
|
||||||
this.wizardData.hubUpdating = true;
|
this.wizardData.hubUpdating = true;
|
||||||
@ -686,7 +856,7 @@ return view.extend({
|
|||||||
console.log('[Wizard] Selected collections:', selected);
|
console.log('[Wizard] Selected collections:', selected);
|
||||||
|
|
||||||
if (selected.length === 0) {
|
if (selected.length === 0) {
|
||||||
this.goToStep(4);
|
this.goToStep(5);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,7 +881,7 @@ return view.extend({
|
|||||||
this.refreshView();
|
this.refreshView();
|
||||||
|
|
||||||
// Auto-advance after 2 seconds
|
// Auto-advance after 2 seconds
|
||||||
setTimeout(L.bind(function() { this.goToStep(4); }, this), 2000);
|
setTimeout(L.bind(function() { this.goToStep(5); }, this), 2000);
|
||||||
}, this)).catch(L.bind(function(err) {
|
}, this)).catch(L.bind(function(err) {
|
||||||
this.wizardData.installing = false;
|
this.wizardData.installing = false;
|
||||||
ui.addNotification(null, E('p', _('Installation failed: %s').format(err.message)), 'error');
|
ui.addNotification(null, E('p', _('Installation failed: %s').format(err.message)), 'error');
|
||||||
@ -760,8 +930,8 @@ return view.extend({
|
|||||||
this.refreshView();
|
this.refreshView();
|
||||||
|
|
||||||
// Auto-advance after 2 seconds
|
// Auto-advance after 2 seconds
|
||||||
console.log('[Wizard] Auto-advancing to Step 5 in 2 seconds...');
|
console.log('[Wizard] Auto-advancing to Step 6 in 2 seconds...');
|
||||||
setTimeout(L.bind(function() { this.goToStep(5); }, this), 2000);
|
setTimeout(L.bind(function() { this.goToStep(6); }, this), 2000);
|
||||||
}, this)).catch(L.bind(function(err) {
|
}, this)).catch(L.bind(function(err) {
|
||||||
console.error('[Wizard] Configuration error:', err);
|
console.error('[Wizard] Configuration error:', err);
|
||||||
this.wizardData.configuring = false;
|
this.wizardData.configuring = false;
|
||||||
@ -809,7 +979,8 @@ return view.extend({
|
|||||||
return API.getBouncers();
|
return API.getBouncers();
|
||||||
}, this)).then(L.bind(function(bouncers) {
|
}, this)).then(L.bind(function(bouncers) {
|
||||||
console.log('[Wizard] Bouncers list:', bouncers);
|
console.log('[Wizard] Bouncers list:', bouncers);
|
||||||
var bouncer = (bouncers || []).find(function(b) {
|
var bouncerList = bouncers && bouncers.bouncers ? bouncers.bouncers : bouncers;
|
||||||
|
var bouncer = (bouncerList || []).find(function(b) {
|
||||||
return b.name === 'crowdsec-firewall-bouncer';
|
return b.name === 'crowdsec-firewall-bouncer';
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -823,13 +994,13 @@ return view.extend({
|
|||||||
this.refreshView();
|
this.refreshView();
|
||||||
|
|
||||||
// Success if enabled, running, and nftables active
|
// Success if enabled, running, and nftables active
|
||||||
// LAPI connection may take a few seconds to establish, so it's optional
|
// LAPI connection may take a few seconds to establish, so it's optional
|
||||||
if (this.wizardData.enabled && this.wizardData.running &&
|
if (this.wizardData.enabled && this.wizardData.running &&
|
||||||
this.wizardData.nftablesActive) {
|
this.wizardData.nftablesActive) {
|
||||||
console.log('[Wizard] All critical services started! Auto-advancing to Step 6...');
|
console.log('[Wizard] All critical services started! Auto-advancing to Step 7...');
|
||||||
ui.addNotification(null, E('p', _('Services started successfully!')), 'info');
|
ui.addNotification(null, E('p', _('Services started successfully!')), 'info');
|
||||||
// Auto-advance after 2 seconds
|
// Auto-advance after 2 seconds
|
||||||
setTimeout(L.bind(function() { this.goToStep(6); }, this), 2000);
|
setTimeout(L.bind(function() { this.goToStep(7); }, this), 2000);
|
||||||
} else {
|
} else {
|
||||||
console.log('[Wizard] Service startup incomplete');
|
console.log('[Wizard] Service startup incomplete');
|
||||||
ui.addNotification(null, E('p', _('Service startup incomplete. Check status and retry.')), 'warning');
|
ui.addNotification(null, E('p', _('Service startup incomplete. Check status and retry.')), 'warning');
|
||||||
|
|||||||
@ -931,10 +931,128 @@ repair_lapi() {
|
|||||||
json_dump
|
json_dump
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Console enrollment status
|
||||||
|
get_console_status() {
|
||||||
|
json_init
|
||||||
|
check_cscli
|
||||||
|
|
||||||
|
local enrolled=0
|
||||||
|
local name=""
|
||||||
|
local company=""
|
||||||
|
|
||||||
|
# Check if console is enrolled by checking if there's a console config
|
||||||
|
if [ -f "/etc/crowdsec/console.yaml" ]; then
|
||||||
|
# Check if the console is actually configured (has credentials)
|
||||||
|
if grep -q "share_manual_decisions\|share_tainted\|share_context" /etc/crowdsec/console.yaml 2>/dev/null; then
|
||||||
|
enrolled=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try to get console status from cscli
|
||||||
|
local console_status
|
||||||
|
console_status=$($CSCLI console status 2>/dev/null)
|
||||||
|
if [ -n "$console_status" ]; then
|
||||||
|
# Check if enrolled by looking for "Enrolled" or similar in output
|
||||||
|
if echo "$console_status" | grep -qi "enrolled\|connected\|active"; then
|
||||||
|
enrolled=1
|
||||||
|
fi
|
||||||
|
# Extract name if present
|
||||||
|
name=$(echo "$console_status" | grep -i "name\|machine" | head -1 | awk -F: '{print $2}' | xargs 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_add_boolean "enrolled" "$enrolled"
|
||||||
|
json_add_string "name" "$name"
|
||||||
|
|
||||||
|
# Get share settings if enrolled
|
||||||
|
if [ "$enrolled" = "1" ] && [ -f "/etc/crowdsec/console.yaml" ]; then
|
||||||
|
local share_manual=$(grep "share_manual_decisions:" /etc/crowdsec/console.yaml 2>/dev/null | awk '{print $2}')
|
||||||
|
local share_tainted=$(grep "share_tainted:" /etc/crowdsec/console.yaml 2>/dev/null | awk '{print $2}')
|
||||||
|
local share_context=$(grep "share_context:" /etc/crowdsec/console.yaml 2>/dev/null | awk '{print $2}')
|
||||||
|
|
||||||
|
json_add_boolean "share_manual_decisions" "$([ \"$share_manual\" = \"true\" ] && echo 1 || echo 0)"
|
||||||
|
json_add_boolean "share_tainted" "$([ \"$share_tainted\" = \"true\" ] && echo 1 || echo 0)"
|
||||||
|
json_add_boolean "share_context" "$([ \"$share_context\" = \"true\" ] && echo 1 || echo 0)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Console enroll
|
||||||
|
console_enroll() {
|
||||||
|
local key="$1"
|
||||||
|
local name="$2"
|
||||||
|
|
||||||
|
json_init
|
||||||
|
check_cscli
|
||||||
|
|
||||||
|
if [ -z "$key" ]; then
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "Enrollment key is required"
|
||||||
|
json_dump
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
secubox_log "Enrolling CrowdSec Console with key..."
|
||||||
|
|
||||||
|
# Build enroll command
|
||||||
|
local enroll_cmd="$CSCLI console enroll $key"
|
||||||
|
if [ -n "$name" ]; then
|
||||||
|
enroll_cmd="$enroll_cmd --name \"$name\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run enrollment
|
||||||
|
local output
|
||||||
|
output=$(eval "$enroll_cmd" 2>&1)
|
||||||
|
local result=$?
|
||||||
|
|
||||||
|
if [ "$result" -eq 0 ]; then
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Successfully enrolled in CrowdSec Console"
|
||||||
|
json_add_string "output" "$output"
|
||||||
|
secubox_log "Console enrollment successful"
|
||||||
|
|
||||||
|
# Enable sharing options by default
|
||||||
|
$CSCLI console enable share_manual_decisions >/dev/null 2>&1
|
||||||
|
$CSCLI console enable share_tainted >/dev/null 2>&1
|
||||||
|
$CSCLI console enable share_context >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
json_add_boolean "success" 0
|
||||||
|
json_add_string "error" "Enrollment failed"
|
||||||
|
json_add_string "output" "$output"
|
||||||
|
secubox_log "Console enrollment failed: $output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
# Console disable (unenroll)
|
||||||
|
console_disable() {
|
||||||
|
json_init
|
||||||
|
check_cscli
|
||||||
|
|
||||||
|
secubox_log "Disabling CrowdSec Console enrollment..."
|
||||||
|
|
||||||
|
# Disable all sharing
|
||||||
|
$CSCLI console disable share_manual_decisions >/dev/null 2>&1
|
||||||
|
$CSCLI console disable share_tainted >/dev/null 2>&1
|
||||||
|
$CSCLI console disable share_context >/dev/null 2>&1
|
||||||
|
|
||||||
|
# Remove console config
|
||||||
|
if [ -f "/etc/crowdsec/console.yaml" ]; then
|
||||||
|
rm -f /etc/crowdsec/console.yaml
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "message" "Console enrollment disabled"
|
||||||
|
secubox_log "Console enrollment disabled"
|
||||||
|
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
# Main dispatcher
|
# Main dispatcher
|
||||||
case "$1" in
|
case "$1" in
|
||||||
list)
|
list)
|
||||||
echo '{"decisions":{},"alerts":{"limit":"number"},"metrics":{},"bouncers":{},"machines":{},"hub":{},"status":{},"ban":{"ip":"string","duration":"string","reason":"string"},"unban":{"ip":"string"},"stats":{},"seccubox_logs":{},"collect_debug":{},"waf_status":{},"metrics_config":{},"configure_metrics":{"enable":"string"},"collections":{},"install_collection":{"collection":"string"},"remove_collection":{"collection":"string"},"update_hub":{},"register_bouncer":{"bouncer_name":"string"},"delete_bouncer":{"bouncer_name":"string"},"firewall_bouncer_status":{},"control_firewall_bouncer":{"action":"string"},"firewall_bouncer_config":{},"update_firewall_bouncer_config":{"key":"string","value":"string"},"nftables_stats":{},"check_wizard_needed":{},"wizard_state":{},"repair_lapi":{}}'
|
echo '{"decisions":{},"alerts":{"limit":"number"},"metrics":{},"bouncers":{},"machines":{},"hub":{},"status":{},"ban":{"ip":"string","duration":"string","reason":"string"},"unban":{"ip":"string"},"stats":{},"seccubox_logs":{},"collect_debug":{},"waf_status":{},"metrics_config":{},"configure_metrics":{"enable":"string"},"collections":{},"install_collection":{"collection":"string"},"remove_collection":{"collection":"string"},"update_hub":{},"register_bouncer":{"bouncer_name":"string"},"delete_bouncer":{"bouncer_name":"string"},"firewall_bouncer_status":{},"control_firewall_bouncer":{"action":"string"},"firewall_bouncer_config":{},"update_firewall_bouncer_config":{"key":"string","value":"string"},"nftables_stats":{},"check_wizard_needed":{},"wizard_state":{},"repair_lapi":{},"console_status":{},"console_enroll":{"key":"string","name":"string"},"console_disable":{}}'
|
||||||
;;
|
;;
|
||||||
call)
|
call)
|
||||||
case "$2" in
|
case "$2" in
|
||||||
@ -1048,6 +1166,18 @@ case "$1" in
|
|||||||
repair_lapi)
|
repair_lapi)
|
||||||
repair_lapi
|
repair_lapi
|
||||||
;;
|
;;
|
||||||
|
console_status)
|
||||||
|
get_console_status
|
||||||
|
;;
|
||||||
|
console_enroll)
|
||||||
|
read -r input
|
||||||
|
key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null)
|
||||||
|
name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
|
||||||
|
console_enroll "$key" "$name"
|
||||||
|
;;
|
||||||
|
console_disable)
|
||||||
|
console_disable
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo '{"error": "Unknown method"}'
|
echo '{"error": "Unknown method"}'
|
||||||
;;
|
;;
|
||||||
|
|||||||
@ -20,7 +20,8 @@
|
|||||||
"firewall_bouncer_config",
|
"firewall_bouncer_config",
|
||||||
"nftables_stats",
|
"nftables_stats",
|
||||||
"check_wizard_needed",
|
"check_wizard_needed",
|
||||||
"wizard_state"
|
"wizard_state",
|
||||||
|
"console_status"
|
||||||
],
|
],
|
||||||
"file": [ "read", "stat" ]
|
"file": [ "read", "stat" ]
|
||||||
},
|
},
|
||||||
@ -40,7 +41,9 @@
|
|||||||
"delete_bouncer",
|
"delete_bouncer",
|
||||||
"control_firewall_bouncer",
|
"control_firewall_bouncer",
|
||||||
"update_firewall_bouncer_config",
|
"update_firewall_bouncer_config",
|
||||||
"repair_lapi"
|
"repair_lapi",
|
||||||
|
"console_enroll",
|
||||||
|
"console_disable"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"uci": [ "crowdsec-dashboard" ]
|
"uci": [ "crowdsec-dashboard" ]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user