Saturday, December 6, 2025

Debian: Different ways to configure network interfaces

To view network adapter drivers, execute:
lspci -k | grep -A2 Ethernet
The list of network interfaces can be seen with the command:
ls /sys/class/net
Let's assume our interface is named "ens33".


Interface configuration using the "ip" utility.

ip link show   #View network connections
ip -s l   #View statistics on sent/received packets
ip address show   #View TCP/IP configuration
ip link set dev ens33 up   #Enable the interface if it was disabled
ip address flush dev ens33   #Reset interface configuration
ip addr add 192.168.0.5/24 broadcast + dev ens33   #Set IP and mask
ip addr del 192.168.0.5/24 dev ens33   #Delete IP and mask (in case of error)
ip route show   #View routing table
ip route add default via 192.168.0.1 dev ens33   #Set default gateway
ip route delete default   #Delete default gateway
ip route add 192.168.10.0/24 via 192.168.0.254 dev ens33   #Add a route
ip route del 192.168.10.0/24 dev ens33 #Delete a route
echo "nameserver 192.168.0.1 8.8.8.8" | sudo tee /etc/resolv.conf   #Add DNS

These settings are temporary and will work until the OS is rebooted.


Configuring the network subsystem using the "ifupdown" method (used by default when installing without a GUI).

1. Install the package and start the service (if the "networking" service is missing):
sudo apt-get install ifupdown
sudo systemctl enable --now networking


2. To configure the interface, you need to create or edit the existing file "/etc/network/interfaces".
Specify the following parameters in the file:

For DHCP:

iface lo inet loopback
auto lo
auto ens33
iface ens33 inet dhcp



For static IP:

iface lo inet loopback
auto lo
auto ens33
iface ens33 inet static
address 192.168.1.100/24
#IP and mask
gateway 192.168.1.1 #Default gateway
up ip route add 10.0.0.0/8 via 192.168.1.200 #Static routes
metric 0

and create the file "/etc/resolv.conf" with the following content:
nameserver 192.168.0.1 #DNS1
nameserver 8.8.8.8 #DNS2


3. Restart the interface:
sudo ifdown ens33 && sudo ifup ens33
or the service:
sudo systemctl restart networking

4. Perform configuration checks:
ip address show
ip route show


If necessary to remove "ifupdown" components, execute:
sudo apt-get remove ifupdown
sudo rm -f
/etc/network/interfaces
sudo rm -rf /etc/network/interfaces.d/*
sudo rm -f /etc/resolv.conf


Configuring the network subsystem "NetworkManager" (used by default when installing with a graphical interface).

1. Install NetworkManager:
sudo apt-get install network-manager

2. Enable and start the service:
sudo systemctl enable --now NetworkManager

3. Configure NetworkManager to manage all interfaces by editing the file "/etc/NetworkManager/NetworkManager.conf": in the [ifupdown] section, change the parameter value to:
managed=true

4. Configure the network interface using the terminal utility "nmtui" or from the command line with the utility "nmcli":

Viewing:
nmcli con show   #View network connections
nmcli dev show ens33  
#View active connection on "ens33"
nmcli con show 'Wired connection 1'  
#View connection parameters

Settings for DHCP:
nmcli con mod 'Wired connection 1' ipv4.method auto
nmcli con mod 'Wired connection 1' ipv4.addresses "" ipv4.gateway ""
nmcli con mod 'Wired connection 1' ipv4.dns ""
nmcli con mod 'Wired connection 1' ipv4.routes ""
nmcli con up 'Wired connection 1'


Settings for static IP:
nmcli con mod 'Wired connection 1' ipv4.addresses 192.168.0.5/24
nmcli con mod 'Wired connection 1' ipv4.gateway 192.168.0.1
nmcli con mod 'Wired connection 1' ipv4.dns "192.168.0.1 8.8.8.8"
nmcli con mod 'Wired connection 1' +ipv4.routes "192.168.10.0/24 192.168.0.254"
nmcli con mod 'Wired connection 1' ipv4.method manual
nmcli con up 'Wired connection 1'


5. Perform configuration checks:
ip address show
ip route show



If necessary to remove "NetworkManager" components, execute:
sudo apt-get remove network-manager
sudo rm -rf /etc/NetworkManager/
sudo rm -rf /var/lib/NetworkManager/
sudo rm -f /etc/resolv.conf


Configuring the network subsystem "systemd-networkd".

1. The "systemd-networkd" service is already included in "systemd", install only "systemd-resolved", add them to autostart:
sudo apt-get install systemd-resolved
sudo systemctl enable --now systemd-networkd
sudo systemctl enable --now systemd-resolved


2. Create a configuration file (the ".network" extension is mandatory):
sudo nano /etc/systemd/network/20-wired.network

Settings for DHCP:

[Match]
Name=ens33
[Network]
DHCP=ipv4



Settings for static IP:

[Match]
Name=ens33
[Network]
Address=192.168.0.5/24
Gateway=192.168.0.1
DNS=192.168.0.1 8.8.8.8
[Route]
Destination=192.168.10.0/24
Gateway=192.168.0.254
Metric=10   #Optional


3. Restart or reload the service:
sudo systemctl restart systemd-networkd
or
sudo networkctl reload

4. Perform configuration checks:
ip address show
ip route show
resolvectl



If necessary to remove "systemd-networkd" components, execute:
sudo systemctl disable --now systemd-networkd systemd-resolved
sudo apt-get remove systemd-
resolved
sudo rm -rf /etc/systemd/network/*

sudo rm -f /etc/resolv.conf


General recommendations!!!
For simple server and virtual machine configurations, use "systemd-networkd", in this case, it is recommended to remove all other modules:
sudo apt-get remove ifupdown network-manager dhcpcd-base
sudo rm -f /etc/network/interfaces
sudo rm -rf /etc/network/interfaces.d/*
sudo rm -rf /etc/NetworkManager/
sudo rm -rf /var/lib/NetworkManager/
sudo rm -f /etc/resolv.conf

Monday, November 3, 2025

Windows: Connecting Huawei Storage System via Dataturbo Protocol

Configuration is performed as follows:

1. Perform preliminary file system configuration on the storage system:

1.1 Create a user for Dataturbo protocol authorization: go to "vStore Service->vStores". Select your vStore (default is System_vStore).


Go to the "User Management" tab and create a user (for example "dataturbo"). Specify the role "vStore DataTurbo administrator".


1.2 Create a logical port: go to "Services->Logical Ports". Select your vStore (default is System_vStore).



Create a new port "DataTurbo_Port", specify the role "Service" and protocol "DataTurbo", configure the IP address, mask, gateway for the port. Set the port type and failover mode.


1.3 Create a file system: go to "Services->File Systems". Select your vStore (default is System_vStore).



Create a file system "DataTurbo_FS", specify the security style "NTFS", volume size and sharing protocol "DataTurbo".


1.4 Configure access to the file system: click on our "DataTurbo_FS" and go to the "Shares" tab, then "DataTurbo Shares".


Select our share "/DataTurbo_FS/" and add read/write permissions to the previously created user "dataturbo".


2. Install the "OceanStor_DataTurbo_1.3.0_Windows.zip" driver on your Windows OS:

2.1 Download "OceanStor_DataTurbo_1.3.0_Windows.zip" and extract the archive to a temporary directory, launch "cmd" with elevated privileges and navigate there.

2.2 Run "install.bat".

2.3 During installation specify:
- driver installation directory;
- performance level depending on available free OS RAM (from 0 to 3);
- username for driver service operation (default - "dataturbo");
- username for driver service management and monitoring (default - "dataturbo_mgmt") and its password.


2.4 Close the console window after installation and reboot the OS.


3. Connect the share via DataTurbo protocol:

3.1 Create a directory within the Windows OS where the storage volume will be mounted (for example, "C:\dataturbo_fs").

3.2 Open the console and check the functionality of the "dataturbo" utility (for example, run "dataturbo show version").

3.3 Connect to the logical port on the storage system, use DNS name or IP configured in the previous step:
dataturbo create storage_object storage_name=OceanProtect ip_list=192.168.1.20

View connected storage systems:
dataturbo show storage_object

To delete storage system:
dataturbo delete storage_object storage_name=OceanProtect

3.4 Mount the share to the previously created directory:
dataturbo mount storage_object storage_name=OceanProtect filesystem_name=/DataTurbo_FS/ mount_dir=C:\dataturbo_fs

View current mounted shares:
dataturbo show mount_dir

To unmount the share (if necessary):
dataturbo umount mount_dir=C:\dataturbo_fs

3.5 After reboot, the share will need to be remounted. To automate this process, you can add the following script to autostart:

@echo off
timeout /t 5
dataturbo mount storage_object storage_name=OceanProtect filesystem_name=/DataTurbo_FS/ mount_dir=C:\dataturbo_fs

The script must be run as a user who is on the DataTurbo driver's whitelist. Open the "whitelist" file (by default located in "C:\Program Files\oceanstor\dataturbo\conf") in a text editor and check if your user account's SID is already on the list. If not, add it.

Tuesday, October 7, 2025

Debian: Configuring for Active Directory

Follow these steps to set up your Debian Linux OS to work with Active Directory:

1. Pre-configuration of network, DNS, full and short hostname.
Ensure the server has correct network settings and can resolve domain names:
ping dc.YOUR_FQDN
nslookup YOUR_FQDN


Replace the local domain name "localdomain" with your domain name YOUR_FQDN in the "/etc/hosts" file using the command:
sudo sed -i 's/localdomain/YOUR_FQDN/g' /etc/hosts

2. Installation of necessary packages.
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt -y install realmd \
sssd sssd-tools libnss-sss libpam-sss adcli samba-common-bin \
oddjob oddjob-mkhomedir packagekit krb5-user


3. Domain discovery and configuration of Kerberos and realmd.
Discover the domain:
sudo realm discover YOUR_FQDN

To configure Kerberos, edit the file "/etc/krb5.conf":

[libdefaults]
    default_realm = YOUR_FQDN
    dns_lookup_realm = true
    dns_lookup_kdc = true


To configure realmd, create the file "/etc/realmd.conf" with the following content:

[service]
automatic-install = yes

[active-directory]
os-name = Debian
os-version = 13


These parameters will be reflected in the computer account properties in Active Directory after joining the host to the domain.

4. Pre-configuration of SSSD.
Since the "/etc/sssd/sssd.conf" file will be overwritten when joining or leaving the domain using the "realm join" command, the best option is to create a new SSSD configuration file with custom parameters.
sudo nano /etc/sssd/conf.d/10-mysettings.conf

[domain/YOUR_FQDN]
# Add authentication provider
auth_provider = ad
# Add password change provider
chpass_provider = ad
# Short username mode, when set to "false"
use_fully_qualified_names = true
# Default shell setting
default_shell = /bin/bash
# Home directory formation rule
fallback_homedir = /home/%u@%d
# Add to ignore group policies that are inaccessible
ad_gpo_ignore_unreadable = true
# Add dynamic DNS settings
dyndns_update = true
dyndns_refresh_interval = 1800
dyndns_ttl = 1800
dyndns_update_ptr = true


Set correct permissions on the configuration file:
sudo chmod 600 /etc/sssd/conf.d/10-mysettings.conf

5. Joining the domain.
sudo realm join YOUR_FQDN
\
--membership-software=adcli \
--user=DomainAdmin


Enter the user password when prompted.

Check successful join:
id DomainAdmin@YOUR_FQDN

6. Configuring automatic home directory creation.
Edit the file "/etc/pam.d/common-session":
sudo nano /etc/pam.d/common-session

Add to the end of the file:
session optional pam_mkhomedir.so skel=/etc/skel umask=077

7. Configuring access rights.

Managing console access to the server:

Deny login to all domain users:
sudo realm deny --all
Allow login only to specific groups:
sudo realm permit -g 'Linux_Console_Users'@YOUR_FQDN
For groups with spaces in the name:
sudo realm permit -g '"Domain Linux Users"'@YOUR_FQDN
Check settings:
sudo realm list
To remove a group from access, use:
sudo realm permit -g -x 'Linux_Console_Users'@YOUR_FQDN

Managing SSH access:

Create a new file in the "/etc/ssh/sshd_config.d" directory:
sudo nano /etc/ssh/sshd_config.d/10-admins.conf

Add parameters to restrict access by groups:
AllowGroups
Linux_SSH_Users@YOUR_FQDN "Linux SSH Admins"@YOUR_FQDN

IMPORTANT!!!!
If you use the parameter value "use_fully_qualified_names = false" in the "/etc/sssd/sssd.conf" file, then when configuring SSH access, specify the short username or group name without the domain name, for example:
AllowGroups Linux_SSH_Users "Linux SSH Admins"

Also, the group names (Linux_SSH_Users and "Linux SSH Admins") and domain name (YOUR_FQDN) must be STRICTLY IN LOWERCASE!!!

Set correct permissions on the file and restart the SSH service:
sudo chmod 600 /etc/ssh/sshd_config.d/10-admins.conf
sudo systemctl restart ssh

8. Configuring sudo rights for domain groups.
Create a sudo rules file:
sudo visudo -f /etc/sudoers.d/domain_admins
Always use the "visudo" command, which prevents saving configuration with syntax errors.

Add necessary rules:

Full administrator rights (example):
%Linux_Sudoers@YOUR_FQDN ALL=(ALL:ALL) ALL
Members of the Linux_Sudoers group can now run commands with "sudo" by entering their Active Directory account password.

Limited rights (example):
%Linux_Sudoers@YOUR_FQDNALL=(root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx

For groups with spaces in the name:
%Linux\ Admins@YOUR_FQDN ALL=(ALL:ALL) ALL

Set file permissions:
sudo chmod 440 /etc/sudoers.d/domain_admins

IMPORTANT!!!!
If you use the parameter value "use_fully_qualified_names = false" in the "/etc/sssd/sssd.conf" file, then when configuring sudoers, specify the short username or group name without the domain name, for example:
%Linux_Sudoers ALL=(ALL:ALL) ALL

9. Testing the configuration.
Try logging in with a domain account:
ssh linux_admin@YOUR_FQDN@server_ip_address
Check home directory creation:
pwd
Check "sudo" functionality for users from allowed groups.
Ensure the server time is synchronized with the domain (use NTP).
Regularly check authentication logs: /var/log/auth.log.
Use SSSD logs for troubleshooting: /var/log/sssd/
Check connectivity with the domain controller:
klist
Check the ability to obtain a Kerberos ticket:
kinit


Removing a host from the domain, renaming a domain machine.

To remove your host from the domain, execute:
sudo realm leave YOUR_FQDN -v --user=DomainAdmin
This will reset all settings made in the "/etc/sssd/sssd.conf" file.

The renaming operation for Linux boils down to the following steps:
1. Remove the host from the domain.
2. Rename using the command:
sudo hostnamectl set-hostname NEW-HOSTNAME
Also change the name in the "/etc/hosts" file:
sudo sed -i 's/OLD-HOSTNAME/NEW-HOSTNAME/g' /etc/hosts
Restart the service:
sudo systemctl restart systemd-hostnamed
3. Re-join the domain with the new name:
sudo realm join YOUR_FQDN \
--membership-software=adcli \
--user=DomainAdmin

After joining the domain, you must reconfigure access using the "sudo realm permit" command, as the "/etc/sssd/sssd.conf" file, which stored the previous settings, was reset by the SSSD service when leaving the domain.

Wednesday, October 1, 2025

Windows: Installing Windows 11 bypassing TPM verification and Secure Boot

If you need to deploy Microsoft Windows 11 on older hardware or a virtual machine without a TPM module and Secure Boot feature for specific reasons, you can bypass this restriction during installation as follows.

Once the graphical setup wizard loads, press the "Shift+F10" key combination: a Windows command prompt window will appear. You can now type the "regedit" command and launch the Registry Editor.

Next, add the following parameters:
1. Create a new key named "HKEY_LOCAL_MACHINE\SYSTEM\Setup\LabConfig".
2. Inside the newly created key, add two parameters of the "DWORD" type:
"BypassTPMCheck" and "BypassSecureBootCheck".
3. Set the value of these parameters to "1".

After modifying the registry, close the editor and continue with the Windows 11 OS installation.

Monday, September 22, 2025

Debian: Setting up a full Debian repository mirror

Creating a full mirror of the Debian repository is a task that requires significant resources and a reliable internet connection. A full mirror includes all packages, updates, backports, and security updates for a specific Debian release.
Debmirror is a specialized Perl utility designed for creating and maintaining Debian and Ubuntu repository mirrors. Unlike standard "rsync" methods, it offers a simpler and more reliable way to synchronize, especially for selective mirroring.

Below are the steps for setting up a Debian 13 ("trixie") repository mirror using "Debmirror":

1. Installing Debmirror and Apache2 web server:
sudo apt-get update
sudo apt-get install debmirror apache2 -y


The basic syntax of the debmirror command is as follows:

debmirror [OPTIONS] /path/to/local/mirror

Key debmirror parameters:
-a, --arch : Architectures (e.g., amd64,i386);
-s, --section : Repository sections (main, contrib, non-free for Debian; main, restricted, universe, multiverse for Ubuntu);
-d, --dist : Distributions (code names, e.g., bullseye, bookworm, trixie for Debian; focal, jammy for Ubuntu). Multiple can be specified separated by commas, including updates (bullseye-updates, bullseye-security);
-h, --host : Source mirror URL (e.g., ftp.debian.org, archive.ubuntu.com or a local Nexus repository);
-r, --root : Root path on the remote server (e.g., /debian for Debian, /ubuntu for Ubuntu);
-e, --method : Download protocol (http, https, ftp, rsync). http or https are often the most reliable.
--progress : Show download progress.
--nosource : Do not download package source code (significantly saves space).
--ignore-release-gpg : Ignore GPG signature verification of Release files. Use with caution as this reduces security.
--ignore-missing-release : Continue synchronization even if the Release file is missing on the server.


2. Test synchronization:

Create repository storage space on disk:
sudo mkdir -p /srv/mirror/debian
sudo mkdir -p /srv/mirror/debian-security
sudo chown -R nobody:nogroup /srv/mirror/debian
sudo chown -R nobody:nogroup /srv/mirror/debian-security


Then run synchronization commands as a test:

Synchronization of the main Debian repository (trixie,trixie-updates,trixie-backports):
debmirror \
-a amd64 \
--nosource \
-s main,contrib,non-free,non-free-firmware \
-d trixie,trixie-updates,trixie-backports \
-h ftp.debian.org \
-r /debian \
-e https \
--progress \
--ignore-release-gpg \
--no-check-gpg \
--rsync-extra=none \
/srv/mirror/debian


Synchronization of Debian security patches (trixie-security):
debmirror \
-a amd64 \
--nosource \
-s main,contrib,non-free,non-free-firmware \
-d trixie-security \
-h security.debian.org \
-r /debian-security \
-e https \
--progress \
--ignore-release-gpg \
--no-check-gpg \
--rsync-extra=none \
/srv/mirror/debian-security


If everything completed without errors, proceed to the next step.


3. Configuring the web server (Apache2) for access to the local repository:

To make the mirror available via HTTP create a file:
sudo nano /etc/apache2/sites-available/debian-mirror.conf

with the following content:

<VirtualHost *:80>
    ServerName your-mirror-domain.com #Replace with your server name or IP
    DocumentRoot /srv/mirror
    <Directory "/srv/mirror">
        Options +Indexes +FollowSymLinks
        IndexOptions NameWidth=* +SuppressDescription FancyIndexing
        Require all granted
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/debian-mirror_error.log
    CustomLog ${APACHE_LOG_DIR}/debian-mirror_access.log combined
</VirtualHost>


If HTTPS support is needed:

Create directories for the key pair (if they don't exist):
sudo mkdir -p /etc/ssl/private /etc/ssl/certs

Then generate a certificate:
sudo openssl req -x509 -nodes -days 730 -newkey rsa:2048 \ -keyout /etc/ssl/private/your-mirror.key \ -out /etc/ssl/certs/your-mirror.crt

During generation, specify Common Name: your-mirror-domain.com. Other fields can be filled as desired or left blank.

Set correct permissions for the key:
sudo chmod 600 /etc/ssl/private/your-mirror.key

And create a file:
sudo nano /etc/apache2/sites-available/debian-mirror-ssl.conf

with the following content:

<VirtualHost *:443>
    ServerName your-mirror-domain.com #Replace with your server name or IP
    DocumentRoot /srv/mirror
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/your-mirror.crt
    SSLCertificateKeyFile /etc/ssl/private/your-mirror.key
    <Directory "/srv/mirror">
        Options +Indexes +FollowSymLinks
        IndexOptions NameWidth=* +SuppressDescription FancyIndexing
        Require all granted
    </Directory>
    ErrorLog ${APACHE_LOG_DIR}/debian-mirror_ssl_error.log
    CustomLog ${APACHE_LOG_DIR}/debian-mirror_ssl_access.log combined
</VirtualHost>


Activate the configuration and restart Apache:
sudo a2ensite debian-mirror
sudo a2ensite debian-mirror-ssl
#For HTTPS support
sudo apachectl configtest
sudo a2enmod rewrite
sudo a2enmod ssl
#For HTTPS support
sudo systemctl restart apache2


Check accessibility: Open "http://your-server-ip/debian" in a browser. The repository directory structure should be displayed.


4. Creating a script to automate the process and monitoring:

The following script can not only perform synchronization of the required repository branches using the "debmirror" command but also log activities, check if the script was previously running, and send reports via email. Create a file:
sudo nano /usr/local/bin/update-mirror.sh

Add the script text to it and make it executable:
sudo chmod +x /usr/local/bin/update-mirror.sh

#!/bin/bash

# Script settings
LOG_FILE="/var/log/mirror_update.log"
LOCK_FILE="/var/run/mirror_update.lock"
MIRROR_BASE="/srv/mirror"

# Email notifications
EMAIL_RECIPIENTS="admin@example.com,devops@example.com"  # Replace with real addresses
EMAIL_SUBJECT_PREFIX="[Mirror Update] "
SMTP_SERVER="Input_IP_of_SMTP_Server"  # SMTP server address
FROM_EMAIL="mirror-update@$(hostname)"  # Sender address

# Mirror configurations
declare -A MIRRORS=(
    ["main"]="
        -a amd64
        --nosource
        -s main,contrib,non-free,non-free-firmware
        -d trixie,trixie-updates,trixie-backports
        -h ftp.by.debian.org
        -r /debian
        -e rsync
        --progress
        --ignore-release-gpg
        --no-check-gpg
        ${MIRROR_BASE}/debian
    "
    ["security"]="
        -a amd64
        --nosource
        -s main,contrib,non-free,non-free-firmware
        -d trixie-security
        -h ftp.by.debian.org
        -r /debian-security
        -e rsync
        --progress
        --ignore-release-gpg
        --no-check-gpg
        ${MIRROR_BASE}/debian-security
    "
)

# Logging function
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S'): $1" >> "$LOG_FILE"
}

# Email sending function using curl and SMTP
send_email() {
    local subject="$1"
    local body="$2"

    # Check if curl is installed
    if command -v curl &> /dev/null; then
        # Form email in MIME format
        local email_content=$(cat <<EOF
From: ${FROM_EMAIL}
To: ${EMAIL_RECIPIENTS}
Subject: ${EMAIL_SUBJECT_PREFIX}${subject}
Date: $(date -R)
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"

${body}
EOF
)

        # Send via SMTP using curl
        echo "$email_content" | curl -s --url "smtp://${SMTP_SERVER}" \
            --mail-from "${FROM_EMAIL}" \
            --mail-rcpt "${EMAIL_RECIPIENTS}" \
            --upload-file - \
            --insecure

        if [ $? -eq 0 ]; then
            log "Email sent successfully: ${subject}"
        else
            log "ERROR: Failed to send email: ${subject}"
        fi
    elif command -v sendmail &> /dev/null; then
        # Alternative method using sendmail
        (
            echo "Subject: ${EMAIL_SUBJECT_PREFIX}${subject}"
            echo "From: ${FROM_EMAIL}"
            echo "To: ${EMAIL_RECIPIENTS}"
            echo
            echo "${body}"
        ) | sendmail -t
    else
        log "ERROR: No email client found. Cannot send notification: $subject"
    fi
}

# Lock check function
check_lock() {
    if [ -e "$LOCK_FILE" ]; then
        local pid=$(cat "$LOCK_FILE")
        if kill -0 "$pid" 2>/dev/null; then
            log "ERROR: Script is already running with PID $pid"
            send_email "Script already running" "Mirror update script is already running with PID $pid. Current execution blocked."
            exit 1
        else
            log "WARNING: Lock file found but process does not exist. Removing lock file."
            rm -f "$LOCK_FILE"
        fi
    fi
}

# Lock creation function
create_lock() {
    echo $$ > "$LOCK_FILE"
}

# Lock removal function
remove_lock() {
    rm -f "$LOCK_FILE"
}

# Debmirror execution function
run_mirror() {
    local name="$1"
    local options="$2"

    log "Starting mirror update: $name"
    log "Command: debmirror $options"

    # Command execution
    debmirror $options

    local exit_code=$?
    if [ $exit_code -eq 0 ]; then
        log "Update successful: $name"
        return 0
    else
        log "Update failed with exit code $exit_code: $name"
        return $exit_code
    fi
}

# Main logic
main() {
    log "=== Starting mirror update process ==="

    # Check lock
    check_lock

    # Create lock
    create_lock

    # Add cleanup handler on exit
    trap 'remove_lock; log "Script interrupted"; exit 1' INT TERM
    trap 'remove_lock' EXIT

    local overall_success=true

    for mirror_name in "${!MIRRORS[@]}"; do
        if ! run_mirror "$mirror_name" "${MIRRORS[$mirror_name]}"; then
            overall_success=false
            log "ERROR: Mirror $mirror_name failed with exit code $?"
            send_email "Update failed: $mirror_name" "Mirror update for $mirror_name failed. Check $LOG_FILE for details."
        fi
    done

    if [ "$overall_success" = true ]; then
        log "=== All mirror updates completed successfully ==="
        send_email "All updates completed" "All mirror updates completed successfully. Check $LOG_FILE for details."
    else
        log "=== Some mirror updates failed ==="
        # Error notifications were already sent for each specific failure
    fi
}

# Error handling
set -euo pipefail

# Launch main function
main "$@"


5. Setting up log rotation:

Create a file /etc/logrotate.d/update_mirror with the following content:

/var/log/mirror_update.log {
weekly
missingok
rotate 26
compress
delaycompress
notifempty
create 644 root root
dateext
dateformat -%Y-%m-%d
}


Explanation of directives:
weekly - rotation occurs once a week;
missingok - skip rotation if the file is missing;
rotate 26 - keep 26 archive copies (half a year ≈ 26 weeks);
compress - compress archive logs using gzip;
delaycompress - delay compression until the next rotation (convenient for debugging);
notifempty - do not rotate empty files;
create 644 root root - create a new log file with permissions 644 and owner root:root after rotation;
dateext - add date to the archive file name;
dateformat -%Y-%m-%d - date format in the file name (YYYY-MM-DD).

Configuration check:
sudo logrotate -d /etc/logrotate.d/update_mirror
The -d key enables debug mode, which will show potential problems without performing rotation.

Forced rotation for testing:
sudo logrotate -vf /etc/logrotate.d/update_mirror
The -v key enables verbose output, and -f forcibly starts rotation.


6. Setting up automatic synchronization via cron

To keep the mirror up to date, it needs to be regularly synchronized. It is recommended to do this at least 4 times a day.
Create a cron job (sudo crontab -e):
Synchronization every 6 hours (at random minutes for even load distribution):
0 */6 * * * /usr/local/bin/update-mirror.sh

or synchronization daily at 2 AM:
0 2 * * * /usr/local/bin/update-mirror.sh

Do not synchronize the mirror exactly at 3:00, 9:00, 15:00, and 21:00 UTC, as this is when the main mirrors are updated. Add a random offset of a few minutes.


7. Testing and using the mirror:

Test your mirror by configuring sources.list on another machine:
sudo nano /etc/apt/sources.list

Add the lines:
deb http://your-mirror-ip-or-domain/debian/ trixie main contrib non-free non-free-firmware
deb http://your-mirror-ip-or-domain/debian/ trixie-updates main contrib non-free non-free-firmware
deb http://your-mirror-ip-or-domain/debian/ trixie-backports main contrib non-free non-free-firmware
deb http://your-mirror-ip-or-domain/debian-security/ trixie-security main contrib non-free non-free-firmware


Update package indexes:
sudo apt update
sudo apt upgrade


Monday, September 8, 2025

ALT Linux: Configuring VMware VM Customization Specifications for ALT Linux OS

Unfortunately, when using "VM Customization Specifications" when deploying ALT Linux OS, "open-vm-toos" scripts cannot correctly configure parameters such as host name, network settings, etc. for this system. To solve this problem, we will use a script that will take over the functionality that does not work correctly in the tools themselves.

Let's perform the following steps to configure "VM Customization Specifications":

1. Prepare an ATL Linux OS template with the installation of the required software, which should already be included with new hosts.
2. Be sure to install "open-vm-tools" and check that the "vmtoolsd" service is in autorun:
sudo apt-get install open-vm-tools
sudo systemctl enable vmtoolsd && sudo systemctl start vmtoolsd


3. Enable support for customization scripts:
sudo vmware-toolbox-cmd config set deployPkg enable-custom-scripts true

To check the current state of the parameter, run:
sudo vmware-toolbox-cmd config get deployPkg enable-custom-scripts

4. Next, configure "VM Customization Specifications" on vCenter Server. When setting up, we specify: OS type - Linux, host name generation rule, time zone, TCP/IP settings and add the following script:

#!/bin/bash

# Function to convert subnet mask to CIDR prefix
mask2cidr() {
    local mask=$1
    local n=0
    IFS=.
    for byte in $mask; do
        case $byte in
            255) n=$((n+8));;
            254) n=$((n+7));;
            252) n=$((n+6));;
            248) n=$((n+5));;
            240) n=$((n+4));;
            224) n=$((n+3));;
            192) n=$((n+2));;
            128) n=$((n+1));;
            0);;
            *) echo 24; return 1;;
        esac
    done
    echo $n
}

# Function to determine network management method
get_network_manager() {
    local interface=$1
    local options_file="/etc/net/ifaces/$interface/options"

    # Check if configuration file exists
    if [ -f "$options_file" ]; then
        local nm_controlled=$(grep -E "^NM_CONTROLLED=" "$options_file" | cut -d= -f2)
        local disabled=$(grep -E "^DISABLED=" "$options_file" | cut -d= -f2)
        local systemd_controlled=$(grep -E "^SYSTEMD_CONTROLLED=" "$options_file" | cut -d= -f2)

        if [ "$nm_controlled" = "yes" ]; then
            echo "NetworkManager"
        elif [ "$disabled" = "no" ]; then
            echo "EtcNet"
        elif [ "$systemd_controlled" = "yes" ]; then
            echo "SystemD"
        else
            echo "Unknown"
        fi
    else
        # If file doesn't exist, check active services
        if systemctl is-active NetworkManager >/dev/null 2>&1; then
            echo "NetworkManager"
        elif systemctl is-active systemd-networkd >/dev/null 2>&1 && \
             systemctl is-active systemd-resolved >/dev/null 2>&1; then
            echo "SystemD"
        elif systemctl is-active network >/dev/null 2>&1; then
            echo "EtcNet"
        else
            echo "Unknown"
        fi
    fi
}

# Function to configure NetworkManager
configure_networkmanager() {
    local interface=$1
    local bootproto=$2
    local ipaddr=$3
    local netmask=$4
    local gateway=$5
    local dns_servers=$6
    local domain=$7

    # Create or modify connection
    if [ "$bootproto" = "static" ]; then
        local prefix_length=$(mask2cidr "$netmask")
        nmcli con add type ethernet con-name "$interface" ifname "$interface" ip4 "$ipaddr/$prefix_length" gw4 "$gateway"
        if [ -n "$dns_servers" ]; then
            nmcli con mod "$interface" ipv4.dns "$dns_servers"
        fi
        if [ -n "$domain" ]; then
            nmcli con mod "$interface" ipv4.dns-search "$domain"
        fi
    else
        nmcli con add type ethernet con-name "$interface" ifname "$interface" ipv4.method auto
    fi

    # Activate connection
    nmcli con up "$interface"
}

# Function to configure EtcNet
configure_etcnet() {
    local interface=$1
    local bootproto=$2
    local ipaddr=$3
    local netmask=$4
    local gateway=$5
    local dns_servers=$6
    local domain=$7

    local iface_dir="/etc/net/ifaces/$interface"

    mkdir -p "$iface_dir"

    # Update options file based on configuration type
    if [ -f "$iface_dir/options" ]; then
        # Update existing options file, preserving other parameters
        if [ "$bootproto" = "static" ]; then
            # Remove old BOOTPROTO and SYSTEMD_BOOTPROTO parameters if they exist
            sed -i '/^BOOTPROTO=/d' "$iface_dir/options"
            sed -i '/^SYSTEMD_BOOTPROTO=/d' "$iface_dir/options"
            # Add new parameters
            echo "BOOTPROTO=static" >> "$iface_dir/options"
            echo "SYSTEMD_BOOTPROTO=static" >> "$iface_dir/options"
        else
            # Remove old BOOTPROTO and SYSTEMD_BOOTPROTO parameters if they exist
            sed -i '/^BOOTPROTO=/d' "$iface_dir/options"
            sed -i '/^SYSTEMD_BOOTPROTO=/d' "$iface_dir/options"
            # Add new parameters
            echo "BOOTPROTO=dhcp" >> "$iface_dir/options"
            echo "SYSTEMD_BOOTPROTO=dhcp4" >> "$iface_dir/options"
        fi
    else
        # Create new options file
        if [ "$bootproto" = "static" ]; then
            cat > "$iface_dir/options" <<EOF
BOOTPROTO=static
SYSTEMD_BOOTPROTO=static
EOF
        else
            cat > "$iface_dir/options" <<EOF
BOOTPROTO=dhcp
SYSTEMD_BOOTPROTO=dhcp4
EOF
        fi
    fi

    if [ "$bootproto" = "static" ]; then
        # Write IP address with CIDR prefix
        PREFIX_LENGTH=$(mask2cidr "$netmask")
        if [ -z "$PREFIX_LENGTH" ]; then
            PREFIX_LENGTH=24
        fi
        echo "$ipaddr/$PREFIX_LENGTH" > "$iface_dir/ipv4address"

        # Write default route
        if [ -n "$gateway" ]; then
            echo "default via $gateway" > "$iface_dir/ipv4route"
        fi

        # Configure DNS via resolv.conf in interface directory
        if [ -n "$dns_servers" ]; then
            # Create resolv.conf in interface directory
            echo "# Generated by network configuration script" > "$iface_dir/resolv.conf"
            if [ -n "$domain" ]; then
                echo "domain $domain" >> "$iface_dir/resolv.conf"
                echo "search $domain" >> "$iface_dir/resolv.conf"
            fi

            # Add DNS servers
            for dns in $dns_servers; do
                echo "nameserver $dns" >> "$iface_dir/resolv.conf"
            done
        fi

        # Release DHCP lease for interface
        if command -v /sbin/dhcpcd >/dev/null 2>&1; then
            /sbin/dhcpcd -k "$interface" 2>/dev/null || true
        fi
    else
        # For DHCP, clear possible previous static configurations
        rm -f "$iface_dir/ipv4address"
        rm -f "$iface_dir/ipv4route"
        rm -f "$iface_dir/resolv.conf"

        # Don't create type file as specified in requirements
    fi

    # Add interface to processing order
    local order_file="/etc/net/ifaces.order"
    if ! grep -q "^$interface$" "$order_file" 2>/dev/null; then
        echo "$interface" >> "$order_file"
    fi

    # Restart network
    systemctl restart network
}

# --- Function to clean logs and temporary files ---
clean_system() {
    # Clean logs (preserving directory structure)
    find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
    find /var/log -type f -name "*.gz" -delete;
    find /var/log -type f -name "*.old" -delete;
    find /var/log -type f -name "lastlog" -exec rm -f {} \;

    # Clean temporary files
    rm -rf /tmp/*
    rm -rf /var/tmp/*

    # Clean command history and user data
    for user_home in /home/*; do
        if [[ -d "$user_home" ]]; then
            user=$(basename "$user_home")
            # Clean bash_history and other histories
            truncate -s 0 "$user_home/.bash_history" 2>/dev/null || true
            # Clean application caches
            rm -rf "$user_home/.cache/*" 2>/dev/null || true
        fi
    done
    # Clean root
    truncate -s 0 /root/.bash_history 2>/dev/null || true
    rm -rf /root/.cache/* 2>/dev/null || true

    # Remove random seed files
    rm -f /var/lib/systemd/random-seed
}

# --- Function to remove SSH host keys ---
reset_ssh_keys() {
    rm -f /etc/ssh/ssh_host_*
    # Keys will be generated on next SSH server start
}

if [ "$1" = "precustomization" ]; then
    # Perform system cleanup
    clean_system
    reset_ssh_keys

    # Check and create /etc/sysconfig/network-scripts directory if needed
    if [ ! -d "/etc/sysconfig/network-scripts" ]; then
        mkdir -p /etc/sysconfig/network-scripts
    fi
    # Pre-customization stage
    VMCUST_DIR=$(ls -d /var/run/.vmware-imgcust* 2>/dev/null | head -n 1)
    if [ -n "$VMCUST_DIR" ]; then
        CUST_CFG_PATH="$VMCUST_DIR/cust.cfg"
        if [ -f "$CUST_CFG_PATH" ]; then
            cp "$CUST_CFG_PATH" "/root/cust.cfg"
        fi
    fi
elif [ "$1" = "postcustomization" ]; then
    # Post-customization stage
    if [ ! -f "/root/cust.cfg" ]; then
        exit 1
    fi

    CFG_FILE="/root/cust.cfg"

    # Parse configuration parameters
    BOOTPROTO=$(awk -F' = ' '/^BOOTPROTO/ {line=$2} END{print tolower(line)}' "$CFG_FILE")
    IPADDR=$(awk -F' = ' '/^IPADDR/ {line=$2} END{print line}' "$CFG_FILE")
    NETMASK=$(awk -F' = ' '/^NETMASK/ {line=$2} END{print line}' "$CFG_FILE")
    GATEWAY=$(awk -F' = ' '/^GATEWAY/ {line=$2} END{print line}' "$CFG_FILE")
    HOSTNAME=$(awk -F' = ' '/^HOSTNAME/ {line=$2} END{print line}' "$CFG_FILE")
    DOMAIN=$(awk -F' = ' '/^DOMAINNAME/ {line=$2} END{print line}' "$CFG_FILE")
    MACADDR=$(awk -F' = ' '/^MACADDR/ {line=$2} END{print tolower(line)}' "$CFG_FILE")
    DNS_SERVERS=$(awk -F' = ' '/^NAMESERVER\|[0-9]/ {print $2}' "$CFG_FILE" | tr '\n' ' ')
    DNS_FROM_DHCP=$(awk -F' = ' '/^DNSFROMDHCP/ {line=$2} END{print tolower(line)}' "$CFG_FILE")
    TIMEZONE=$(awk -F' = ' '/^TIMEZONE/ {line=$2} END{print line}' "$CFG_FILE")
    UTC=$(awk -F' = ' '/^UTC/ {line=$2} END{print tolower(line)}' "$CFG_FILE")

    # Determine network interface
    INTERFACE=$(ip -o link | awk -v mac="$MACADDR" 'tolower($0) ~ mac {gsub(":", "", $2); print $2}')
    if [ -z "$INTERFACE" ]; then
        INTERFACE=$(ip route | awk '/default/ {print $5; exit}')
    fi

    # Determine network management method
    NET_MGR=$(get_network_manager "$INTERFACE")

    case "$NET_MGR" in
        "NetworkManager")
            configure_networkmanager "$INTERFACE" "$BOOTPROTO" "$IPADDR" "$NETMASK" "$GATEWAY" "$DNS_SERVERS" "$DOMAIN"
            ;;
        "EtcNet")
            configure_etcnet "$INTERFACE" "$BOOTPROTO" "$IPADDR" "$NETMASK" "$GATEWAY" "$DNS_SERVERS" "$DOMAIN"
            ;;
        "SystemD")
            # Existing SystemD configuration
            rm -f /etc/systemd/network/*.network
            rm -f /etc/systemd/resolved.conf.d/*.conf

            # Create base configuration for loopback interface
            cat > /etc/systemd/network/00-loopback.network <<EOF
[Match]
Name=lo

[Network]
Address=127.0.0.1/8
Address=::1/128
EOF

            # Create configuration for main interface
            CONFIG_FILE="/etc/systemd/network/alterator-${INTERFACE}.network"

            if [ "$BOOTPROTO" = "static" ]; then
                if [ -z "$IPADDR" ] || [ -z "$NETMASK" ]; then
                    exit 1
                fi

                PREFIX_LENGTH=$(mask2cidr "$NETMASK")
                if [ -z "$PREFIX_LENGTH" ]; then
                    PREFIX_LENGTH=24
                fi

                # Create config for static IP with DNS
                cat > "$CONFIG_FILE" <<EOF
[Match]
Name=$INTERFACE

[Network]
Address=$IPADDR/$PREFIX_LENGTH
Gateway=$GATEWAY
EOF

                # Add DNS servers if needed
                if { [ "$BOOTPROTO" = "static" ] || [ "$DNS_FROM_DHCP" = "no" ]; } && [ -n "$DNS_SERVERS" ]; then
                    for dns in $DNS_SERVERS; do
                        echo "DNS=$dns" >> "$CONFIG_FILE"
                    done
                fi
            else
                # Create config for DHCP
                cat > "$CONFIG_FILE" <<EOF
[Match]
Name=$INTERFACE

[Network]
DHCP=ipv4

[DHCPv4]
UseDomains=true
UseDNS=yes
EOF

                # Add DNS servers if not using DNS from DHCP
                if [ "$DNS_FROM_DHCP" = "no" ] && [ -n "$DNS_SERVERS" ]; then
                    for dns in $DNS_SERVERS; do
                        echo "DNS=$dns" >> "$CONFIG_FILE"
                    done
                fi
            fi

            systemctl restart systemd-networkd
            systemctl restart systemd-resolved
            ;;
        *)
            echo "Unknown network management method"
            exit 1
            ;;
    esac

    # Common settings
    hostnamectl set-hostname "$HOSTNAME"
    sed -i "/127\.0\.1\.1/d" /etc/hosts
    echo "127.0.1.1 $HOSTNAME.$DOMAIN $HOSTNAME" >> /etc/hosts

     # Timezone configuration
    if [ -n "$TIMEZONE" ]; then
        timedatectl set-timezone "$TIMEZONE"
    fi

    # BIOS time format configuration (UTC or local)
    if [ -n "$UTC" ]; then
        if [ "$UTC" = "yes" ]; then
            # Set UTC time in BIOS
            timedatectl set-local-rtc 0
        else
            # Set local time in BIOS
            timedatectl set-local-rtc 1
        fi
    fi

    # Save configuration
    {
        echo "CONFIG_SOURCE=$CFG_FILE"
        echo "BOOTPROTO=$BOOTPROTO"
        echo "INTERFACE=$INTERFACE"
        echo "HOSTNAME=$HOSTNAME"
        echo "DOMAIN=$DOMAIN"
        echo "TIMEZONE=$TIMEZONE"
        echo "UTC=$UTC"
        [ -n "$DNS_FROM_DHCP" ] && echo "DNS_FROM_DHCP=$DNS_FROM_DHCP"
        [ -n "$DNS_SERVERS" ] && echo "DNS_SERVERS=\"$DNS_SERVERS\""
    }

    if [ "$BOOTPROTO" = "static" ]; then
        {
            echo "IP_ADDRESS=$IPADDR"
            echo "NETMASK=$NETMASK"
            echo "PREFIX_LENGTH=$PREFIX_LENGTH"
            [ -n "$GATEWAY" ] && echo "GATEWAY=$GATEWAY"
        }
    fi
    rm -f "/root/cust.cfg"
##############################################################################################
    # PLACE FOR ADDITIONAL SYSTEM CONFIGURATION
    # Here you can add commands for final machine setup:
    # - Installation of required software
    # - Configuration via Ansible or other configuration management systems
    # - Execution of custom scripts
    # - Setup of monitoring, logging and other services
##############################################################################################
fi

This script covers all possible network setup options: EtcNet, NetworkManager, Systemd-Networkd, both with static IP and with DHCP.

Wednesday, June 18, 2025

Windows: Enable LDAP over SSL (LDAPS) using a third-party certificate authority (CA)

Let's consider the option of configuring the LDAPS (LDAP over SSL) protocol using a third-party certification authority for the mydomain.local domain with three controllers: DC1, DC2, DC3:

1. Create a certificate request template in the form of a <name>.inf file for each domain controller. Replace <name> with the FQDN of the controller (for example, DC1.mydomain.local):

[Version]
Signature="$Windows NT$"

[NewRequest]
Subject = "CN=DC1.mydomain.local" ; FQDN of the current controller
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0

[EnhancedKeyUsageExtension]
OID=1.3.6.1.5.5.7.3.1 ; Server Authentication

[Extensions]
2.5.29.17 = "{text}"
_continue_ = "dns=DC1.mydomain.local&" ; FQDN of the current controller
_continue_ = "dns=DC1&" ; short name of the current controller
_continue_ = "dns=DC2.mydomain.local&" ; FQDN of other controllers
_continue_ = "dns=DC2&"
_continue_ = "dns=DC3.mydomain.local&"
_continue_ = "dns=DC3&"
_continue_ = "dns=ldap.mydomain.local&" ; virtual name for switching
_continue_ = "dns=mydomain.local&" ; FQDN
_continue_ = "dns=MYDOMAIN" ; NetBIOS domain name

Important:
For balancing, you need to add the FQDN of the domain and all domain controllers, as well as their NetBIOS names, to the [Extensions] section. You can also add fault-tolerant DNS records, such as "ldap.mydomain.local".

Save the file on each controller (for example, C:\Cert\DC1.inf).

2. Generate a CSR for each controller. To do this, create certificate requests via the command line (on each controller):
certreq -new C:\Cert\<name>.inf C:\Cert\<name>.csr

3. Now you need to send the CSR to a third-party CA. Transfer the DC1.csr, DC2.csr, DC3.csr files to the administrator of the external CA and get back the signed certificates (DC1.cer, DC2.cer, DC3.cer or DC1.p7b, DC2.p7b, DC3.p7b) from the CA.

4. Install the certificates on the domain controllers. To do this, run the import command on each domain controller:
certreq -accept C:\Cert\<name>.cer

To check the installed certificate, open "mmc", add the snap-in "Certificates (Local Computer)". Make sure that the certificate is displayed in Personal → Certificates and has a private key.

5. To complete the LDAPS configuration on the controllers with the firewall enabled, open port 636:
New-NetFirewallRule -DisplayName "LDAPS" -Direction Inbound -Protocol TCP -LocalPort 636 -Action Allow

6. Restart the LDAP service or reboot the servers:
Restart-Service NTDS -Force

7. Check the operation of LDAPS using "ldp.exe" - connect to the domain controller, specifying port 636 and checking the "SSL" box.

Disabling unencrypted LDAP (optional):

Open "Group Policy Management" and select "Default Domain Controllers Policy".
Go to: "Computer Configuration → Policies → Windows Settings → Security Settings → Domain Controller: Require digital signature for LDAP server".
Set the parameter: "Require digital signature".
Restart the domain controllers to apply the changes.