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
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.
LaZy IT
Tuesday, October 7, 2025
Debian: Configuring for Active Directory
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):
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.
Monday, May 26, 2025
Windows: Remove recovery partition from disk
1. First, disable the recovery environment:
reagentc /disable
2. Then run:
diskpart
list volume
Pay attention to the volume number for the recovery partition, remember it.
3. Delete the partition:
select volume N
delete volume override
exit
where N - the number of your recovery partition.
As a result of these actions, the recovery partition will be deleted and the space it occupied will be freed up.
Friday, May 23, 2025
Windows: Disabling Azure Arc Setup on Windows Server 2022 and later
For Windows 2022:
Disable-WindowsOptionalFeature -Online -FeatureName AzureArcSetup
(Also for Windows 2022, you can disable this feature by disabling components using the "Server Manager")
For Windows 2025:
DISM /online /Remove-Capability /CapabilityName:AzureArcSetup~~~~
After this, you will need to reboot the host.