mirror of https://github.com/Aidaho12/haproxy-wi
parent
97f5781313
commit
bc4875e670
|
@ -19,6 +19,7 @@ A simple web interface(user-frendly web GUI) for managing Haproxy servers. Leave
|
|||
13. Users roles: admin, editor, viewer
|
||||
14. Server groups
|
||||
15. Telegram notification
|
||||
16. Creating HA HAProxy cluster
|
||||
|
||||
# Install
|
||||
The installer will ask you a few questions
|
||||
|
@ -40,6 +41,7 @@ $ chmod +x haproxy-wi/cgi-bin/*.py
|
|||
|
||||
For Apache do virtualhost with cgi-bin. Like this:
|
||||
```
|
||||
# vi /etc/httpd/conf.d/haproxy-wi.conf
|
||||
<VirtualHost *:8080>
|
||||
ServerName haproxy-wi
|
||||
ErrorLog /var/log/httpd/haproxy-wi.error.log
|
||||
|
|
|
@ -146,7 +146,7 @@ for i in listhap:
|
|||
print('<option value="%s">%s</option>' % (i[2], i[1]))
|
||||
|
||||
print('</select>'
|
||||
'<div class="tooltip tooltipTop"><b>Note:</b> If you reconfigure First server, second will reconfigured automatically</div>'
|
||||
'<div class="tooltip tooltipTop"><b>Note:</b> If you reconfigure Master server, Slave will reconfigured automatically</div>'
|
||||
'</td>'
|
||||
'</tr>'
|
||||
'<tr>'
|
||||
|
@ -159,8 +159,8 @@ print('</select>'
|
|||
'<td class="addName">IP and Port:</td>'
|
||||
'<td class="addOption">'
|
||||
'<input type="text" name="ip" id="ip" title="" size="15" placeholder="172.28.0.1" class="form-control"><b>:</b>'
|
||||
'<input type="number" name="port" id="listen-port" required title="Port for bind listner" size="5" placeholder="8080" class="form-control">'
|
||||
'<div class="tooltip tooltipTop">IP for bind listner, <b>if empty will be assignet on all IPs</b>. Start typing ip, or press down.</div>'
|
||||
'<input type="number" name="port" id="listen-port" required title="Port for bind listen" size="5" placeholder="8080" class="form-control">'
|
||||
'<div class="tooltip tooltipTop">IP for bind listner, <b>if empty will be assignet on all IPs</b>. Start typing ip, or press down.<br>If you use <b>VRRP keep in blank</b>. If you assign an IP, the slave will not start</div>'
|
||||
'</td>'
|
||||
'</tr>'
|
||||
'<tr>'
|
||||
|
@ -266,7 +266,7 @@ for i in listhap:
|
|||
print('<option value="%s">%s</option>' % (i[2], i[1]))
|
||||
|
||||
print('</select>'
|
||||
'<div class="tooltip tooltipTop"><b>Note:</b> If you reconfigure First server, second will reconfigured automatically</div>'
|
||||
'<div class="tooltip tooltipTop"><b>Note:</b> If you reconfigure Master server, Slave will reconfigured automatically</div>'
|
||||
'</td>'
|
||||
'</tr>'
|
||||
'<tr>'
|
||||
|
@ -279,8 +279,8 @@ print('</select>'
|
|||
'<td class="addName">IP and Port:</td>'
|
||||
'<td class="addOption">'
|
||||
'<input type="text" name="ip" id="ip1" size="15" placeholder="172.28.0.1" class="form-control"><b>:</b>'
|
||||
'<input type="number" name="port" required title="Port for bind listner" placeholder="8080" class="form-control">'
|
||||
'<div class="tooltip tooltipTop">IP for bind listner, <b>if empty will be assignet on all IPs</b>. Start typing ip, or press down.</div>'
|
||||
'<input type="number" name="port" required title="Port for bind frontend" placeholder="8080" class="form-control">'
|
||||
'<div class="tooltip tooltipTop">IP for bind listner, <b>if empty will be assignet on all IPs</b>. Start typing ip, or press down.<br>If you use <b>VRRP keep in blank</b>. If you assign an IP, the slave will not start</div>'
|
||||
'</td>'
|
||||
'</tr>'
|
||||
'<tr>'
|
||||
|
@ -350,7 +350,7 @@ for i in listhap:
|
|||
print('<option value="%s">%s</option>' % (i[2], i[1]))
|
||||
|
||||
print('</select>'
|
||||
'<div class="tooltip tooltipTop"><b>Note:</b> If you reconfigure First server, second will reconfigured automatically</div>'
|
||||
'<div class="tooltip tooltipTop"><b>Note:</b> If you reconfigure Master server, Slave will reconfigured automatically</div>'
|
||||
'</td>'
|
||||
'</tr>'
|
||||
'<tr>'
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-"
|
||||
import cgi
|
||||
import os
|
||||
import paramiko
|
||||
|
@ -15,7 +16,6 @@ config.read(path_config)
|
|||
form = cgi.FieldStorage()
|
||||
serv = form.getvalue('serv')
|
||||
fullpath = config.get('main', 'fullpath')
|
||||
log_path = config.get('main', 'log_path')
|
||||
time_zone = config.get('main', 'time_zone')
|
||||
ssh_keys = config.get('ssh', 'ssh_keys')
|
||||
ssh_user_name = config.get('ssh', 'ssh_user_name')
|
||||
|
@ -45,6 +45,8 @@ def logging(serv, action):
|
|||
cookie = http.cookies.SimpleCookie(os.environ.get("HTTP_COOKIE"))
|
||||
login = cookie.get('login')
|
||||
mess = now_utc.strftime(dateFormat) + " from " + IP + " user: " + login.value + " " + action + " for: " + serv + "\n"
|
||||
log_path = config.get('main', 'log_path')
|
||||
|
||||
try:
|
||||
log = open(log_path + "/config_edit-"+get_data('logs')+".log", "a")
|
||||
log.write(mess)
|
||||
|
@ -180,6 +182,8 @@ def links():
|
|||
'<li><a href=/cgi-bin/add.py#ssl title="Upload SSL cert" class="cert head-submenu">SSL</a></li>'
|
||||
'<li><a href=/cgi-bin/config.py title="Edit Config" class="edit head-submenu">Edit</a> </li>')
|
||||
print('</li>')
|
||||
if is_admin():
|
||||
print('<li><a title="Create HA cluster" class="ha">HA</a>')
|
||||
if is_admin(level = 2):
|
||||
print('<li><a title="Actions with configs" class="version">Versions</a>'
|
||||
'<li><a href=/cgi-bin/configver.py title="Upload old versions configs" class="upload head-submenu">Upload</a></li>')
|
||||
|
@ -189,7 +193,7 @@ def links():
|
|||
print('</li>')
|
||||
show_login_links()
|
||||
if is_admin():
|
||||
print('<li><a title="Admin area" class="version">Admin area</a>'
|
||||
print('<li><a title="Admin area" class="admin">Admin area</a>'
|
||||
'<li><a href=/cgi-bin/users.py#users title="Actions with users" class="users head-submenu">Users</a></li>'
|
||||
'<li><a href=/cgi-bin/users.py#groups title="Actions with groups" class="group head-submenu">Groups</a></li>'
|
||||
'<li><a href=/cgi-bin/users.py#servers title="Actions with servers" class="runtime head-submenu">Servers</a></li>'
|
||||
|
@ -199,7 +203,7 @@ def links():
|
|||
'</li>')
|
||||
print('</ul>'
|
||||
'</nav>'
|
||||
'<div class="copyright-menu">HAproxy-WI v2.0.8</div>'
|
||||
'<div class="copyright-menu">HAproxy-WI v2.1</div>'
|
||||
'</div>')
|
||||
|
||||
def show_login_links():
|
||||
|
@ -534,7 +538,7 @@ def ssh_command(serv, commands, **kwargs):
|
|||
else:
|
||||
print('<div style="margin: -10px;">'+stdout.read().decode(encoding='UTF-8')+'</div>')
|
||||
|
||||
print(stderr.read().decode(encoding='UTF-8'))
|
||||
print(stderr.read().decode(encoding='UTF-8')+"<br/>")
|
||||
|
||||
ssh.close()
|
||||
|
||||
|
@ -578,6 +582,6 @@ def chooseServer(formName, title, note, **kwargs):
|
|||
print('</p></form>')
|
||||
|
||||
if note == "y":
|
||||
print('<div class="alert alert-info"><b>Note:</b> If you reconfigure First server, second will reconfigured automatically</div>')
|
||||
print('<div class="alert alert-info"><b>Note:</b> If you reconfigure Master server, Slave will reconfigured automatically</div>')
|
||||
print('</center>')
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python3
|
||||
import html
|
||||
import cgi
|
||||
import funct
|
||||
import sql
|
||||
from configparser import ConfigParser, ExtendedInterpolation
|
||||
|
||||
funct.head("HA")
|
||||
funct.check_login()
|
||||
funct.page_for_admin()
|
||||
|
||||
path_config = "haproxy-webintarface.config"
|
||||
config = ConfigParser(interpolation=ExtendedInterpolation())
|
||||
config.read(path_config)
|
||||
serv = ""
|
||||
|
||||
print('<script src="/inc/users.js"></script>'
|
||||
'<h2>Configure HA</h2>'
|
||||
'<table class="overview">'
|
||||
'<tr class="overviewHead">'
|
||||
'<td class="padding10 first-collumn">Master</td>'
|
||||
'<td>Slave</td>'
|
||||
'<td>VRRP interface</td>'
|
||||
'<td>VRRP IP</td>'
|
||||
'<td>Install HAProxy</td>'
|
||||
'<td></td>'
|
||||
'</tr>'
|
||||
'<tr>'
|
||||
'<td class="padding10 first-collumn">'
|
||||
'<select id="master">'
|
||||
'<option disable selected>Choose master</option>')
|
||||
funct.choose_only_select(serv)
|
||||
print('</select>'
|
||||
'</td>'
|
||||
'<td>'
|
||||
'<select id="slave">'
|
||||
'<option disable selected>Choose master</option>')
|
||||
funct.choose_only_select(serv)
|
||||
print('</select>'
|
||||
'</td>'
|
||||
'<td>'
|
||||
'<input type="text" id="interface" class="form-control">'
|
||||
'</td>'
|
||||
'<td>'
|
||||
'<input type="text" id="vrrp-ip" class="form-control">'
|
||||
'</td>'
|
||||
'<td>'
|
||||
'<label for="hap"></label><input type="checkbox" checked id="hap">'
|
||||
'</td>'
|
||||
'<td>'
|
||||
'<a class="ui-button ui-widget ui-corner-all" id="create" title="Create HA configuration">Create</a>'
|
||||
'</td>'
|
||||
'</table>'
|
||||
'<div id="ajax"></div>')
|
||||
|
|
@ -58,7 +58,7 @@ haproxy_config_path = ${haproxy_dir}/haproxy.cfg
|
|||
server_state_file = ${haproxy_dir}/haproxy.state
|
||||
haproxy_sock = /var/run/haproxy.sock
|
||||
#Temp store configs, for haproxy check
|
||||
tmp_config_path = /tmp
|
||||
tmp_config_path = /tmp/
|
||||
cert_path = /etc/ssl/certs/
|
||||
#If enable this option Haproxy-wi will be configure firewalld based on config port
|
||||
firewall_enable = 1
|
|
@ -1,7 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-"
|
||||
import html
|
||||
import cgi
|
||||
import os
|
||||
import os, sys
|
||||
import json
|
||||
import subprocess
|
||||
import funct
|
||||
|
@ -202,11 +203,16 @@ if serv is not None and act == "configShow":
|
|||
|
||||
if form.getvalue('viewlogs') is not None:
|
||||
viewlog = form.getvalue('viewlogs')
|
||||
try:
|
||||
log_path = config.get('main', 'log_path')
|
||||
except:
|
||||
print('<div class="alert alert-warning">Please check the config for the presence of the parameter - "log_path". </div>')
|
||||
|
||||
try:
|
||||
log = open(log_path + viewlog, "r")
|
||||
except IOError:
|
||||
print('<div class="alert alert-danger">Can\'t read import config file</div>')
|
||||
print('<div class="alert alert-danger">Can\'t read import log file</div>')
|
||||
sys.exit()
|
||||
|
||||
print('<center><h3>Shows log: %s</h3></center><br />' % viewlog)
|
||||
i = 0
|
||||
|
@ -217,3 +223,26 @@ if form.getvalue('viewlogs') is not None:
|
|||
else:
|
||||
print('<div class="line">' + line + '</div>')
|
||||
|
||||
if form.getvalue('master'):
|
||||
master = form.getvalue('master')
|
||||
slave = form.getvalue('slave')
|
||||
interface = form.getvalue('interface')
|
||||
vrrpip = form.getvalue('vrrpip')
|
||||
hap = form.getvalue('hap')
|
||||
tmp_config_path = config.get('haproxy', 'tmp_config_path')
|
||||
script = "install_keepalived.sh"
|
||||
|
||||
os.system("cp scripts/%s ." % script)
|
||||
|
||||
|
||||
funct.upload(master, tmp_config_path, script)
|
||||
funct.upload(slave, tmp_config_path, script)
|
||||
|
||||
commands = [ "chmod +x "+tmp_config_path+script, tmp_config_path+script+" MASTER "+interface+" "+vrrpip+" "+hap ]
|
||||
funct.ssh_command(master, commands)
|
||||
|
||||
commands = [ "chmod +x "+tmp_config_path+script, tmp_config_path+script+" BACKUP "+interface+" "+vrrpip+" "+hap ]
|
||||
funct.ssh_command(slave, commands)
|
||||
|
||||
os.system("rm -f %s" % script)
|
||||
sql.update_server_master(master, slave)
|
|
@ -0,0 +1,75 @@
|
|||
#!/bin/bash
|
||||
CONF=/etc/keepalived/keepalived.conf
|
||||
|
||||
if [[ $4 == 1 ]];then
|
||||
yum install haproxy -y > /dev/null
|
||||
systemctl enable haproxy
|
||||
systemctl restart haproxy
|
||||
fi
|
||||
|
||||
yum install keepalived -y > /dev/null
|
||||
if [ $? -eq 1 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
echo "" > $CONF
|
||||
|
||||
cat << EOF > $CONF
|
||||
global_defs {
|
||||
router_id LVS_DEVEL
|
||||
}
|
||||
|
||||
#health-check for keepalive
|
||||
vrrp_script chk_haproxy { # Requires keepalived-1.1.13
|
||||
script "pidof haproxy"
|
||||
interval 2 # check every 2 seconds
|
||||
weight 3 # addA 3 points of prio if OK
|
||||
}
|
||||
|
||||
vrrp_instance VI_1 {
|
||||
state MASTER
|
||||
interface eth0
|
||||
virtual_router_id 100
|
||||
priority 102
|
||||
|
||||
#check if we are still running
|
||||
track_script {
|
||||
chk_haproxy
|
||||
}
|
||||
|
||||
advert_int 1
|
||||
authentication {
|
||||
auth_type PASS
|
||||
auth_pass VerySecretPass
|
||||
}
|
||||
virtual_ipaddress {
|
||||
0.0.0.0
|
||||
}
|
||||
}
|
||||
EOF
|
||||
if [ $? -eq 1 ]
|
||||
then
|
||||
echo "Can't read keepalived config"
|
||||
exit 1
|
||||
fi
|
||||
sed -i "s/MASTER/$1/g" $CONF
|
||||
sed -i "s/eth0/$2/g" $CONF
|
||||
sed -i "s/0.0.0.0/$3/g" $CONF
|
||||
|
||||
if [[ $1 == "BACKUP" ]];then
|
||||
sed -i "s/102/103/g" $CONF
|
||||
fi
|
||||
|
||||
systemctl enable keepalived
|
||||
systemctl restart keepalived
|
||||
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
|
||||
sysctl -p
|
||||
firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --in-interface enp0s8 --destination 224.0.0.18 --protocol vrrp -j ACCEPT
|
||||
firewall-cmd --direct --permanent --add-rule ipv4 filter OUTPUT 0 --out-interface enp0s8 --destination 224.0.0.18 --protocol vrrp -j ACCEPT
|
||||
firewall-cmd --reload
|
||||
|
||||
if [ $? -eq 1 ]
|
||||
then
|
||||
echo "Can't start keepalived"
|
||||
exit 1
|
||||
fi
|
|
@ -162,6 +162,26 @@ def update_server(hostname, ip, group, typeip, enable, master, id):
|
|||
cur.close()
|
||||
con.close()
|
||||
|
||||
def update_server_master(master, slave):
|
||||
con, cur = create_db.get_cur()
|
||||
sql = """ select id from servers where ip = '%s' """ % master
|
||||
try:
|
||||
cur.execute(sql)
|
||||
con.commit()
|
||||
except sqltool.Error as e:
|
||||
print('<span class="alert alert-danger" id="error">An error occurred: ' + e.args[0] + ' <a title="Close" id="errorMess"><b>X</b></a></span>')
|
||||
con.rollback()
|
||||
for id in cur.fetchall():
|
||||
sql = """ update servers set master = '%s' where ip = '%s' """ % (id[0], slave)
|
||||
try:
|
||||
cur.execute(sql)
|
||||
con.commit()
|
||||
except sqltool.Error as e:
|
||||
print('<span class="alert alert-danger" id="error">An error occurred: ' + e.args[0] + ' <a title="Close" id="errorMess"><b>X</b></a></span>')
|
||||
con.rollback()
|
||||
cur.close()
|
||||
con.close()
|
||||
|
||||
def select_users(**kwargs):
|
||||
con, cur = create_db.get_cur()
|
||||
sql = """select * from user ORDER BY id"""
|
||||
|
@ -301,9 +321,11 @@ def get_dick_permit(**kwargs):
|
|||
cur.close()
|
||||
con.close()
|
||||
|
||||
def is_master(ip):
|
||||
def is_master(ip, **kwargs):
|
||||
con, cur = create_db.get_cur()
|
||||
sql = """ select slave.ip from servers left join servers as slave on servers.id = slave.master where servers.ip = '%s' """ % ip
|
||||
if kwargs.get('master_slave'):
|
||||
sql = """ select master.hostname, master.ip, slave.hostname, slave.ip from servers as master left join servers as slave on master.id = slave.master where slave.master > 0 """
|
||||
try:
|
||||
cur.execute(sql)
|
||||
except sqltool.Error as e:
|
||||
|
|
|
@ -10,17 +10,19 @@ form = cgi.FieldStorage()
|
|||
viewlog = form.getvalue('viewlogs')
|
||||
|
||||
funct.head("View logs")
|
||||
funct.check_config()
|
||||
funct.check_login()
|
||||
funct.page_for_admin()
|
||||
funct.get_auto_refresh("View logs")
|
||||
|
||||
path_config = "haproxy-webintarface.config"
|
||||
config = ConfigParser(interpolation=ExtendedInterpolation())
|
||||
config.read(path_config)
|
||||
|
||||
try:
|
||||
if config.get('main', 'log_path'):
|
||||
log_path = config.get('main', 'log_path')
|
||||
|
||||
funct.page_for_admin()
|
||||
funct.get_auto_refresh("View logs")
|
||||
except:
|
||||
print('<center><div class="alert alert-danger">Can not find "log_path" parametr. Check into config</div>')
|
||||
try:
|
||||
os.chdir(log_path)
|
||||
except IOError:
|
||||
|
|
|
@ -27,7 +27,7 @@ print('<br />'
|
|||
'<select autofocus required name="serv" id="serv">'
|
||||
'<option disabled>Choose server</option>')
|
||||
|
||||
funct.choose_only_select(serv, virt=1)
|
||||
funct.choose_only_select(serv, master_slave=1)
|
||||
|
||||
print('</select>'
|
||||
'<a class="ui-button ui-widget ui-corner-all" id="show" title="Show stats" onclick="showStats()">Show</a>'
|
||||
|
|
|
@ -66,6 +66,11 @@
|
|||
font-family: "Font Awesome 5 Solid";
|
||||
content: "\f044";
|
||||
}
|
||||
.ha::before {
|
||||
display: none;
|
||||
font-family: "Font Awesome 5 Solid";
|
||||
content: "\f0c2";
|
||||
}
|
||||
.version::before {
|
||||
display: none;
|
||||
font-family: "Font Awesome 5 Solid";
|
||||
|
@ -91,6 +96,11 @@
|
|||
font-family: "Font Awesome 5 Solid";
|
||||
content: "\f01e";
|
||||
}
|
||||
.admin::before {
|
||||
display: none;
|
||||
font-family: "Font Awesome 5 Solid";
|
||||
content: "\f21b";
|
||||
}
|
||||
.users::before {
|
||||
display: none;
|
||||
font-family: "Font Awesome 5 Solid";
|
||||
|
|
45
inc/users.js
45
inc/users.js
|
@ -15,6 +15,43 @@ jQuery.expr[':'].regex = function(elem, index, match) {
|
|||
}
|
||||
|
||||
$( function() {
|
||||
$('#create').click(function() {
|
||||
var ipformat = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
var hap = 0;
|
||||
if ($('#hap').is(':checked')) {
|
||||
hap = '1';
|
||||
}
|
||||
$("#ajax").html('')
|
||||
if( $("#master").val() == "" || $("#slave").val() == "" || $("#interface").val() == "" ||
|
||||
$("#vrrp-ip").val() == "") {
|
||||
$("#ajax").html('<div class="alert alert-danger">Please fill in all fields</div>')
|
||||
} else if(! $("#vrrp-ip").val().match(ipformat)) {
|
||||
$("#ajax").html('<div class="alert alert-danger">Please enter IP in "VRRP IP" field</div>')
|
||||
} else {
|
||||
$("#ajax").html('<div class="alert alert-warning">Please don\'t close and don\'t represh page. Wait until the work is completed. This may take some time </div>');
|
||||
$.ajax( {
|
||||
url: "options.py",
|
||||
data: {
|
||||
master: $('#master').val(),
|
||||
slave: $('#slave').val(),
|
||||
interface: $("#interface").val(),
|
||||
vrrpip: $('#vrrp-ip').val(),
|
||||
hap: hap
|
||||
},
|
||||
type: "GET",
|
||||
success: function( data ) {
|
||||
data = data.replace(/\s+/g,' ');
|
||||
if (data.indexOf('error') != '-1' || data.indexOf('alert') != '-1' || data.indexOf('Failed') != '-1') {
|
||||
$("#ajax").html('<div class="alert alert-danger">'+data+'</data>');
|
||||
} else if (data.indexOf('success') != '-1' ){
|
||||
$('.alert-danger').remove();
|
||||
$("#ajax").html('<div class="alert alert-success">All is ready!</data>');
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
});
|
||||
|
||||
$('.alert-danger').remove();
|
||||
|
||||
$('#add-user').click(function() {
|
||||
|
@ -316,11 +353,11 @@ function uploadSsh() {
|
|||
$("#ajax-ssh").html(data);
|
||||
} else if (data.indexOf('success') != '-1') {
|
||||
$('.alert-danger').remove();
|
||||
$("#ssh").addClass( "update", 1000 );
|
||||
setTimeout(function() {
|
||||
$( "#ssh").removeClass( "update" );
|
||||
}, 2500 );
|
||||
$("#ajax-ssh").html(data);
|
||||
setTimeout(function() {
|
||||
$( "#ajax-ssh").html( "" );
|
||||
}, 2500 );
|
||||
|
||||
} else {
|
||||
$("#ajax-ssh").html('<div class="alert alert-danger">Something wrong, check and try again</div>');
|
||||
}
|
||||
|
|
|
@ -284,6 +284,7 @@ firewall-cmd --reload
|
|||
|
||||
chmod +x /var/www/$HOME_HAPROXY_WI/cgi-bin/*.py
|
||||
chown -R apache:apache /var/www/$HOME_HAPROXY_WI/
|
||||
rm -f /var/www/$HOME_HAPROXY_WI/log/config_edit.log
|
||||
|
||||
setenforce 0
|
||||
sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
|
||||
|
|
Loading…
Reference in New Issue