From c746f32851e9ad62936e4d64c1caf60d78145efc Mon Sep 17 00:00:00 2001 From: Daniel Berteaud Date: Fri, 22 Aug 2025 17:00:22 +0200 Subject: [PATCH] Update to 2025-08-22 17:00 --- roles/iptables/tasks/install_RedHat.yml | 6 + roles/lemonldap_ng/files/Web.pm | 216 +++++++++++++++++++++++ roles/lemonldap_ng/tasks/main.yml | 4 + roles/lemonldap_ng/vars/RedHat-10.yml | 31 ++++ roles/lemonldap_ng/vars/RedHat-8.yml | 1 + roles/lemonldap_ng/vars/RedHat-9.yml | 1 + roles/repo_base/tasks/AlmaLinux-10.yml | 57 ++++++ roles/repo_base/tasks/epel_RedHat-10.yml | 12 ++ roles/repo_zabbix/defaults/main.yml | 2 +- roles/repo_zabbix/vars/RedHat-10.yml | 3 + 10 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 roles/lemonldap_ng/files/Web.pm create mode 100644 roles/lemonldap_ng/vars/RedHat-10.yml create mode 100644 roles/repo_base/tasks/AlmaLinux-10.yml create mode 100644 roles/repo_base/tasks/epel_RedHat-10.yml create mode 100644 roles/repo_zabbix/vars/RedHat-10.yml diff --git a/roles/iptables/tasks/install_RedHat.yml b/roles/iptables/tasks/install_RedHat.yml index 4bc9e77..e60a90e 100644 --- a/roles/iptables/tasks/install_RedHat.yml +++ b/roles/iptables/tasks/install_RedHat.yml @@ -8,3 +8,9 @@ yum: name=iptables-services tags: firewall + # EL10 comes with python 3.12 which removed distutils + # distutils is needed by iptables_raw.py, so install setuptools which bring distutils back +- name: Install setuptools + package: name=python3-setuptools + when: ansible_distribution_version is version(10, '>=') + tags: firewall diff --git a/roles/lemonldap_ng/files/Web.pm b/roles/lemonldap_ng/files/Web.pm new file mode 100644 index 0000000..5b906b4 --- /dev/null +++ b/roles/lemonldap_ng/files/Web.pm @@ -0,0 +1,216 @@ +package Lemonldap::NG::Common::MessageBroker::Web; + +use strict; +use IO::Socket::INET; +use IO::Socket::SSL; +use IO::Select; +use Lemonldap::NG::Common::FormEncode; +use Lemonldap::NG::Common::UserAgent; +use JSON; +use Protocol::WebSocket::Client; + +our $VERSION = '2.22.0'; + +use constant DEFAULTWS => 'localhost:8080'; +our $pr = '::MessageBroker::Web:'; + +sub new { + my ( $class, $conf, $logger ) = @_; + my $args = $conf->{messageBrokerOptions} // {}; + my $ssl = ''; + unless ( $args->{server} ) { + $args->{server} = DEFAULTWS; + $logger->info("$pr no server given"); + } + elsif ( $args->{server} =~ m#^(?:(?:http|ws)(s)?://)?([^/:]+:[^/:]+)/?$# ) { + $args->{server} = $2; + $ssl = 1 if $1; + } + else { + $logger->error("$pr unparsable server '$args->{server}'"); + $args->{server} = DEFAULTWS; + } + $logger->debug("$pr using server $args->{server}"); + my $self = bless { + logger => $logger, + server => $args->{server}, + messages => {}, + ssl => $ssl, + token => $args->{token}, + ua => $args->{ua} || Lemonldap::NG::Common::UserAgent->new($conf), + }, $class; + $self->{ua}->env_proxy(); + return $self; +} + +sub publish { + my ( $self, $channel, $msg ) = @_; + die 'Not a hash msg' unless ref $msg eq 'HASH'; + $msg->{channel} = $channel; + my $j = eval { JSON::to_json($msg) }; + if ($@) { + $self->logger->error("$pr message error: $@"); + return; + } + my $req = HTTP::Request->new( + POST => "http$self->{ssl}://$self->{server}/publish", + [ + 'Content-Length' => length($j), + ( + $self->{token} + ? ( Authorization => "Bearer $self->{token}" ) + : () + ) + ], + $j + ); + my $resp = $self->ua->request($req); + $resp->is_success + ? ( $self->logger->debug("$pr publish $msg->{action}") ) + : ( $self->logger->error( "$pr publish error: " . $resp->status_line ) ); +} + +sub subscribe { + my ( $self, $channel ) = @_; + return + if $self->{channels} + and $self->{channels} =~ /^(?:.*,)?$channel(?:,.*)?$/; + $self->{channels} = + $self->{channels} ? "$self->{channels},$channel" : $channel; + $self->{messages}{$channel} = []; + $self->logger->debug("$pr subscribe to $self->{channels}"); + my $sock = $self->_connect; +} + +sub getNextMessage { + my ( $self, $channel ) = @_; + return undef + unless $self->{ws} and defined $self->{messages}{$channel}; + return shift( @{ $self->{messages}{$channel} } ) + if @{ $self->{messages}{$channel} }; + $self->_read_socket; + return shift( @{ $self->{messages}{$channel} } ) + if @{ $self->{messages}{$channel} }; +} + +sub waitForNextMessage { + my ( $self, $channel ) = @_; + return undef + unless $self->{messages}{$channel}; + my $res; + do { + $res = $self->getNextMessage($channel); + sleep 1 unless $res; + } while ( !$res ); + return $res; +} + +sub _connect { + my ($self) = @_; + my ( $host, $port ) = split /:/, $self->{server}; + my $sock = IO::Socket::INET->new( + PeerHost => $host, + PeerPort => $port, + Proto => 'tcp', + Timeout => 5, + ) + or do { + $self->logger->error("$pr Failed to connect to $self->{server}: $!"); + $self->{connected} = 0; + return; + }; + $self->logger->debug("$pr connected"); + + if ( $self->{ssl} ) { + $sock = IO::Socket::SSL->start_SSL( $sock, SSL_verify_mode => 0 ) + or do { + $self->logger->error("$pr SSL error: $!"); + $self->{connected} = 0; + return; + }; + $self->logger->debug("$pr connection upgraded to TLS"); + } + my $url = "ws" . ($self->{ssl} ? 's' : '') . "://$self->{server}/subscribe?" + . build_urlencoded( channels => $self->{channels} ); + $self->logger->debug("$pr connects to $url"); + my $client = Protocol::WebSocket::Client->new( url => $url ); + + $client->on( + read => sub { + my ( $c, $buf ) = @_; + if ( $buf =~ /^{.*}$/ ) { + eval { + my $data = JSON::decode_json($buf); + if ( $data->{channel} + && defined $self->{messages}->{ $data->{channel} } ) + { + push @{ $self->{messages}->{ $data->{channel} } }, + $data; + } + else { + $self->logger->info( + "$pr received a message for an unknown channel"); + } + }; + $self->logger->error("$pr unable to read websocket: $@") + if ($@); + } + else { + $self->logger->warn("$pr received an unreadable message: $buf"); + } + } + ); + + $client->on( + write => sub { + my ( $c, $buf ) = @_; + print $sock $buf; + } + ); + + $client->on( + error => sub { + $self->logger->error("$pr websocket error: $_[1]"); + } + ); + + $client->{hs}->{req}->{headers} = + [ Authorization => "Bearer $self->{token}", ] + if $self->{token}; + + $client->connect(); + + $self->{socket} = $sock; + $self->{selector} = IO::Select->new($sock); + $self->{ws} = $client; + $self->{connected} = 1; +} + +sub _read_socket { + my ($self) = @_; + return unless $self->{connected}; + return unless $self->{selector}->can_read(0.01); + my $sock = $self->{socket}; + my $buf; + my $n = sysread( $sock, $buf, 4096 ); + + if ( !defined $n || $n == 0 ) { + warn "Connection lost, trying to reconnect...\n"; + $self->{connected} = 0; + $self->_connect; + return; + } + + $self->{ws}->read($buf); +} + +# Accessors +sub logger { + return $_[0]->{logger}; +} + +sub ua { + return $_[0]->{ua}; +} + +1; diff --git a/roles/lemonldap_ng/tasks/main.yml b/roles/lemonldap_ng/tasks/main.yml index c575f9e..da532d2 100644 --- a/roles/lemonldap_ng/tasks/main.yml +++ b/roles/lemonldap_ng/tasks/main.yml @@ -55,6 +55,10 @@ when: llng_conf_backend == 'mysql' tags: always +- name: Install MessageBroker::Web + copy: src=Web.pm dest=/usr/share/perl5/vendor_perl/Lemonldap/NG/Common/MessageBroker/Web.pm owner=root group=root mode=644 + tags: web + - name: Deploy Lemonldap::NG main configuration template: src=lemonldap-ng.ini.j2 dest=/etc/lemonldap-ng/lemonldap-ng.ini group=apache mode=640 notify: "{{ (llng_server == 'nginx' and llng_engine == 'uwsgi') | ternary('reload', 'restart') }} {{ (llng_server == 'nginx') | ternary('llng', 'httpd') }}" diff --git a/roles/lemonldap_ng/vars/RedHat-10.yml b/roles/lemonldap_ng/vars/RedHat-10.yml new file mode 100644 index 0000000..e3fa88e --- /dev/null +++ b/roles/lemonldap_ng/vars/RedHat-10.yml @@ -0,0 +1,31 @@ +--- + +llng_common_packages: + - lemonldap-ng-common + - perl-Cache-Cache + - lemonldap-ng-fastcgi-server + - python3-PyMySQL + - uwsgi-plugin-psgi + - uwsgi-logger-systemd + - perl-Protocol-WebSocket + +llng_portal_packages: + - python3-passlib + - lemonldap-ng-portal + - lemonldap-ng-doc + - lasso + - lasso-perl + - perl-Authen-Captcha + - perl-Auth-Yubikey_WebClient + - perl-Authen-WebAuthn + - perl-Glib + +llng_manager_packages: + - lemonldap-ng-manager + - lemonldap-ng-doc + +llng_mysql_packages: + - perl-DBD-MySQL + - python3-PyMySQL + - mariadb + - perl-Apache-Session-Browseable diff --git a/roles/lemonldap_ng/vars/RedHat-8.yml b/roles/lemonldap_ng/vars/RedHat-8.yml index d542101..591c0ff 100644 --- a/roles/lemonldap_ng/vars/RedHat-8.yml +++ b/roles/lemonldap_ng/vars/RedHat-8.yml @@ -7,6 +7,7 @@ llng_common_packages: - python3-mysql - uwsgi-plugin-psgi - uwsgi-logger-systemd + - perl-Protocol-WebSocket llng_portal_packages: - python3-passlib diff --git a/roles/lemonldap_ng/vars/RedHat-9.yml b/roles/lemonldap_ng/vars/RedHat-9.yml index 6765380..e3fa88e 100644 --- a/roles/lemonldap_ng/vars/RedHat-9.yml +++ b/roles/lemonldap_ng/vars/RedHat-9.yml @@ -7,6 +7,7 @@ llng_common_packages: - python3-PyMySQL - uwsgi-plugin-psgi - uwsgi-logger-systemd + - perl-Protocol-WebSocket llng_portal_packages: - python3-passlib diff --git a/roles/repo_base/tasks/AlmaLinux-10.yml b/roles/repo_base/tasks/AlmaLinux-10.yml new file mode 100644 index 0000000..77c4535 --- /dev/null +++ b/roles/repo_base/tasks/AlmaLinux-10.yml @@ -0,0 +1,57 @@ +--- + +- set_fact: + base_repos: + - name: baseos + file: almalinux + dir: BaseOS + - name: appstream + file: almalinux + dir: AppStream + - name: crb + file: almalinux + dir: CRB + - name: extras + file: almalinux + dir: extras + tags: repo + +- name: Configure repositories + yum_repository: + file: "{{ item.file }}" + description: "AlmaLinux {{ item.name }}" + name: "{{ item.name }}" + baseurl: https://repo.almalinux.org/almalinux/$releasever/{{ item.dir }}/$basearch/os/ + gpgcheck: True + gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux-10 + enabled: "{{ item.enabled | default(True) }}" + loop: "{{ base_repos }}" + tags: repo + +- name: Empty default file + yum_repository: + file: almalinux-{{ item.name }} + name: "{{ item.name }}" + state: absent + loop: "{{ base_repos }}" + tags: repo + +#- name: Configure COPR for FusionInventory +# yum_repository: +# name: fusioninventory +# description: Copr repo for FusionInventory +# file: fusioninventory +# baseurl: https://download.copr.fedorainfracloud.org/results/frsoftware/FusionInventory/epel-$releasever-$basearch/ +# gpgcheck: True +# gpgkey: https://download.copr.fedorainfracloud.org/results/frsoftware/FusionInventory/pubkey.gpg +# tags: repo + +- include_tasks: epel_{{ ansible_os_family }}-{{ ansible_distribution_major_version }}.yml + tags: always + +- include_tasks: dbd_{{ ansible_os_family }}.yml + tags: always + +- include_tasks: postgres_client_{{ ansible_os_family }}.yml + tags: always + diff --git a/roles/repo_base/tasks/epel_RedHat-10.yml b/roles/repo_base/tasks/epel_RedHat-10.yml new file mode 100644 index 0000000..65573d8 --- /dev/null +++ b/roles/repo_base/tasks/epel_RedHat-10.yml @@ -0,0 +1,12 @@ +--- + +- name: Configure EPEL repository + yum_repository: + name: epel + description: "Extra Package for Enterprise Linux" + baseurl: https://fr2.rpmfind.net/linux/epel/$releasever/Everything/$basearch https://mirror.in2p3.fr/pub/epel/$releasever/Everything/$basearch + gpgcheck: True + gpgkey: https://mirror.in2p3.fr/pub/epel/RPM-GPG-KEY-EPEL-10 + exclude: dehydrated,zabbix* + tags: repo + diff --git a/roles/repo_zabbix/defaults/main.yml b/roles/repo_zabbix/defaults/main.yml index 9298bca..3118f82 100644 --- a/roles/repo_zabbix/defaults/main.yml +++ b/roles/repo_zabbix/defaults/main.yml @@ -1,2 +1,2 @@ --- -zabbix_major_version: 7.2 +zabbix_major_version: 7.4 diff --git a/roles/repo_zabbix/vars/RedHat-10.yml b/roles/repo_zabbix/vars/RedHat-10.yml new file mode 100644 index 0000000..912dfc3 --- /dev/null +++ b/roles/repo_zabbix/vars/RedHat-10.yml @@ -0,0 +1,3 @@ +--- + +zabbix_repo_key: https://repo.zabbix.com/RPM-GPG-KEY-ZABBIX-B5333005