From: Rich Kreider Date: Mon, 30 Mar 2026 02:30:57 +0000 (-0400) Subject: new posts X-Git-Url: https://git.kreider.io/?a=commitdiff_plain;h=811789096730037b68d80c0d41e8852bb20b6474;p=kreider.io.git new posts --- diff --git a/content/posts/hello.md b/content/posts/hello.md deleted file mode 100644 index 6a5edc7..0000000 --- a/content/posts/hello.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Hello, world. -date: 2026-03-27 ---- -Hello world. diff --git a/content/posts/job_history_email.mdwn b/content/posts/job_history_email.mdwn deleted file mode 100644 index 2c593a8..0000000 --- a/content/posts/job_history_email.mdwn +++ /dev/null @@ -1,381 +0,0 @@ -# SQL Server: Daily Failed SQL Job Email Report - -This guide sets up: - -- Database Mail -- a Database Mail profile -- a stored procedure that emails failed SQL Agent jobs from the last 24 hours -- a SQL Agent job scheduled for **11:59 PM daily** - ---- - -## What this does - -Every night at **11:59 PM**, SQL Server Agent runs a stored procedure in `msdb` that: - -- checks SQL Agent job history for failed jobs in the last 24 hours -- emails the failed job names, failure time, duration, and failure reason -- sends a "no failures" email if nothing failed - ---- - -## Prerequisites - -You need: - -- SQL Server Agent running -- an SMTP server you can relay through -- the following SMTP details: - - SMTP server name - - SMTP port - - whether SSL/TLS is required - - SMTP username/password if authentication is required - - From address - - recipient email address - ---- - -## Step 1: Enable Database Mail XPs - -Run this first: - -```sql -EXEC sp_configure 'show advanced options', 1; -RECONFIGURE; - -EXEC sp_configure 'Database Mail XPs', 1; -RECONFIGURE; -``` - ---- - -## Step 2: Create the Database Mail account and profile - -Replace the sample values below. - -### Variables you should replace - -- `SQLAlerts` = mail profile name -- `SQLAlertsAccount` = mail account name -- `smtp.yourdomain.com` = SMTP server -- `587` = SMTP port -- `sqlalerts@yourdomain.com` = From address -- `SQL Alerts` = display name -- `smtp-user` = SMTP username -- `smtp-password` = SMTP password - -### Script - -```sql -USE msdb; -GO - --- Create Database Mail account -EXEC dbo.sysmail_add_account_sp - @account_name = N'SQLAlertsAccount', - @description = N'SQL Server Database Mail account for alerts', - @email_address = N'sqlalerts@yourdomain.com', - @display_name = N'SQL Alerts', - @replyto_address = N'sqlalerts@yourdomain.com', - @mailserver_name = N'smtp.yourdomain.com', - @port = 587, - @enable_ssl = 1, - @username = N'smtp-user', - @password = N'smtp-password'; -GO - --- Create Database Mail profile -EXEC dbo.sysmail_add_profile_sp - @profile_name = N'SQLAlerts', - @description = N'Database Mail profile for SQL alerting'; -GO - --- Add account to profile -EXEC dbo.sysmail_add_profileaccount_sp - @profile_name = N'SQLAlerts', - @account_name = N'SQLAlertsAccount', - @sequence_number = 1; -GO - --- Make profile public/default if desired -EXEC dbo.sysmail_add_principalprofile_sp - @profile_name = N'SQLAlerts', - @principal_name = N'public', - @is_default = 1; -GO -``` - -### Notes - -- If your SMTP server does **not** require authentication, remove `@username` and `@password`. -- If your SMTP server uses port 25 and no TLS, set: - - `@port = 25` - - `@enable_ssl = 0` -- If your mail relay only allows specific source IPs or hosts, make sure the SQL Server can relay through it. - ---- - -## Step 3: Test Database Mail - -Run this before doing anything else. - -```sql -EXEC msdb.dbo.sp_send_dbmail - @profile_name = 'SQLAlerts', - @recipients = 'you@domain.com', - @subject = 'SQL Database Mail Test', - @body = 'Database Mail is working.'; -``` - -If this fails, stop there and fix SMTP/profile/authentication first. - ---- - -## Step 4: Create the stored procedure that emails failed jobs - -Replace `you@domain.com` with the real recipient. - -```sql -USE msdb; -GO - -CREATE OR ALTER PROCEDURE dbo.usp_EmailDailyFailedJobs -AS -BEGIN - SET NOCOUNT ON; - - DECLARE @Subject nvarchar(255); - SET @Subject = N'Daily SQL Job Failures - ' + @@SERVERNAME; - - IF EXISTS - ( - SELECT 1 - FROM msdb.dbo.sysjobhistory h - WHERE h.step_id = 0 - AND h.run_status = 0 - AND msdb.dbo.agent_datetime(h.run_date, h.run_time) >= DATEADD(DAY, -1, GETDATE()) - ) - BEGIN - EXEC msdb.dbo.sp_send_dbmail - @profile_name = 'SQLAlerts', - @recipients = 'you@domain.com', - @subject = @Subject, - @body = 'Failed SQL Agent jobs in the last 24 hours:', - @query = N' -SET NOCOUNT ON; - -SELECT - @@SERVERNAME AS ServerName, - j.name AS JobName, - msdb.dbo.agent_datetime(h.run_date, h.run_time) AS FailedAt, - STUFF(STUFF(RIGHT(''000000'' + CAST(h.run_duration AS varchar(6)), 6), 3, 0, '':''), - 6, 0, '':'') AS RunDuration_HHMMSS, - h.message AS FailureReason -FROM msdb.dbo.sysjobhistory h -JOIN msdb.dbo.sysjobs j - ON j.job_id = h.job_id -WHERE - h.step_id = 0 - AND h.run_status = 0 - AND msdb.dbo.agent_datetime(h.run_date, h.run_time) >= DATEADD(DAY, -1, GETDATE()) -ORDER BY FailedAt DESC;', - @execute_query_database = 'msdb', - @query_result_header = 1, - @query_result_separator = ' | ', - @query_result_no_padding = 1; - END - ELSE - BEGIN - EXEC msdb.dbo.sp_send_dbmail - @profile_name = 'SQLAlerts', - @recipients = 'you@domain.com', - @subject = @Subject, - @body = 'No SQL Agent job failures were recorded in the last 24 hours.'; - END -END -GO -``` - ---- - -## Step 5: Test the stored procedure manually - -```sql -EXEC msdb.dbo.usp_EmailDailyFailedJobs; -``` - -If this does not send mail, do not create the scheduled job yet. - ---- - -## Step 6: Create the SQL Agent job scheduled for 11:59 PM daily - -```sql -USE msdb; -GO - -DECLARE @job_id UNIQUEIDENTIFIER; - --- Create the job -EXEC msdb.dbo.sp_add_job - @job_name = N'Daily SQL Job Failure Email', - @enabled = 1, - @description = N'Sends a daily email at 11:59 PM listing failed SQL Agent jobs from the last 24 hours.', - @owner_login_name = N'sa', - @job_id = @job_id OUTPUT; - --- Add the job step -EXEC msdb.dbo.sp_add_jobstep - @job_id = @job_id, - @step_name = N'Run Failure Report', - @subsystem = N'TSQL', - @database_name = N'msdb', - @command = N'EXEC msdb.dbo.usp_EmailDailyFailedJobs;', - @on_success_action = 1, - @on_fail_action = 2; - --- Create the schedule -EXEC msdb.dbo.sp_add_schedule - @schedule_name = N'Daily 11:59 PM', - @enabled = 1, - @freq_type = 4, - @freq_interval = 1, - @active_start_time = 235900; - --- Attach schedule to job -EXEC msdb.dbo.sp_attach_schedule - @job_id = @job_id, - @schedule_name = N'Daily 11:59 PM'; - --- Attach job to local server -EXEC msdb.dbo.sp_add_jobserver - @job_id = @job_id, - @server_name = N'(LOCAL)'; -GO -``` - ---- - -## Step 7: Test the SQL Agent job immediately - -Do not wait until midnight to find out it is broken. - -```sql -EXEC msdb.dbo.sp_start_job - @job_name = 'Daily SQL Job Failure Email'; -``` - ---- - -## Step 8: Check job history - -```sql -EXEC msdb.dbo.sp_help_jobhistory - @job_name = 'Daily SQL Job Failure Email'; -``` - ---- - -## Helpful verification queries - -### Show mail profiles - -```sql -SELECT name, description -FROM msdb.dbo.sysmail_profile; -``` - -### Show which account is tied to which profile - -```sql -SELECT - p.name AS ProfileName, - a.name AS AccountName, - a.email_address, - a.display_name, - s.servername, - s.port -FROM msdb.dbo.sysmail_profile p -JOIN msdb.dbo.sysmail_profileaccount pa - ON p.profile_id = pa.profile_id -JOIN msdb.dbo.sysmail_account a - ON pa.account_id = a.account_id -JOIN msdb.dbo.sysmail_server s - ON a.account_id = s.account_id; -``` - -### Show public/default profiles - -```sql -SELECT - p.name, - pp.is_default, - pp.principal_id -FROM msdb.dbo.sysmail_principalprofile pp -JOIN msdb.dbo.sysmail_profile p - ON pp.profile_id = p.profile_id; -``` - -### Show mail log/errors - -```sql -SELECT * -FROM msdb.dbo.sysmail_event_log -ORDER BY log_date DESC; -``` - -### Show unsent/sent/failed messages - -```sql -SELECT * -FROM msdb.dbo.sysmail_allitems -ORDER BY send_request_date DESC; -``` - ---- - -## Common failure points - -### 1. `sp_send_dbmail` fails -Usually one of these: - -- wrong profile name -- bad SMTP server/port -- auth failure -- relay denied -- TLS/SSL mismatch -- firewall blocking outbound SMTP - -### 2. Job never runs -Usually one of these: - -- SQL Server Agent service is stopped -- schedule was not attached -- job is disabled - -### 3. Stored procedure compiles but sends nothing -Usually one of these: - -- there were no failed jobs in the last 24 hours -- recipient email is wrong -- mail profile is wrong - ---- - -## Blunt recommendation - -Build and test in this order: - -1. enable Database Mail XPs -2. create account/profile -3. test `sp_send_dbmail` -4. create stored procedure -5. run stored procedure manually -6. create Agent job/schedule -7. manually start Agent job -8. trust the 11:59 PM schedule only after the manual test works - -If step 3 fails, everything after it is pointless until SMTP is fixed. - - -[[!tag notes mssql]] diff --git a/content/posts/mssql-job-history-email.md b/content/posts/mssql-job-history-email.md new file mode 100644 index 0000000..565e7e9 --- /dev/null +++ b/content/posts/mssql-job-history-email.md @@ -0,0 +1,386 @@ +--- +title: "SQL Server: Daily Failed SQL Job Email Report" +date: 2026-03-29 +toc: true +tags: ["mssql"] +--- + +This guide sets up: + +- Database Mail +- a Database Mail profile +- a stored procedure that emails failed SQL Agent jobs from the last 24 hours +- a SQL Agent job scheduled for **11:59 PM daily** + +--- + +## What this does + +Every night at **11:59 PM**, SQL Server Agent runs a stored procedure in `msdb` that: + +- checks SQL Agent job history for failed jobs in the last 24 hours +- emails the failed job names, failure time, duration, and failure reason +- sends a "no failures" email if nothing failed + +--- + +## Prerequisites + +You need: + +- SQL Server Agent running +- an SMTP server you can relay through +- the following SMTP details: + - SMTP server name + - SMTP port + - whether SSL/TLS is required + - SMTP username/password if authentication is required + - From address + - recipient email address + +--- + +## Step 1: Enable Database Mail XPs + +Run this first: + +```sql +EXEC sp_configure 'show advanced options', 1; +RECONFIGURE; + +EXEC sp_configure 'Database Mail XPs', 1; +RECONFIGURE; +``` + +--- + +## Step 2: Create the Database Mail account and profile + +Replace the sample values below. + +### Variables you should replace + +- `SQLAlerts` = mail profile name +- `SQLAlertsAccount` = mail account name +- `smtp.yourdomain.com` = SMTP server +- `587` = SMTP port +- `sqlalerts@yourdomain.com` = From address +- `SQL Alerts` = display name +- `smtp-user` = SMTP username +- `smtp-password` = SMTP password + +### Script + +```sql +USE msdb; +GO + +-- Create Database Mail account +EXEC dbo.sysmail_add_account_sp + @account_name = N'SQLAlertsAccount', + @description = N'SQL Server Database Mail account for alerts', + @email_address = N'sqlalerts@yourdomain.com', + @display_name = N'SQL Alerts', + @replyto_address = N'sqlalerts@yourdomain.com', + @mailserver_name = N'smtp.yourdomain.com', + @port = 587, + @enable_ssl = 1, + @username = N'smtp-user', + @password = N'smtp-password'; +GO + +-- Create Database Mail profile +EXEC dbo.sysmail_add_profile_sp + @profile_name = N'SQLAlerts', + @description = N'Database Mail profile for SQL alerting'; +GO + +-- Add account to profile +EXEC dbo.sysmail_add_profileaccount_sp + @profile_name = N'SQLAlerts', + @account_name = N'SQLAlertsAccount', + @sequence_number = 1; +GO + +-- Make profile public/default if desired +EXEC dbo.sysmail_add_principalprofile_sp + @profile_name = N'SQLAlerts', + @principal_name = N'public', + @is_default = 1; +GO +``` + +### Notes + +- If your SMTP server does **not** require authentication, remove `@username` and `@password`. +- If your SMTP server uses port 25 and no TLS, set: + - `@port = 25` + - `@enable_ssl = 0` +- If your mail relay only allows specific source IPs or hosts, make sure the SQL Server can relay through it. + +--- + +## Step 3: Test Database Mail + +Run this before doing anything else. + +```sql +EXEC msdb.dbo.sp_send_dbmail + @profile_name = 'SQLAlerts', + @recipients = 'you@domain.com', + @subject = 'SQL Database Mail Test', + @body = 'Database Mail is working.'; +``` + +If this fails, stop there and fix SMTP/profile/authentication first. + +--- + +## Step 4: Create the stored procedure that emails failed jobs + +Replace `you@domain.com` with the real recipient. + +```sql +USE msdb; +GO + +CREATE OR ALTER PROCEDURE dbo.usp_EmailDailyFailedJobs +AS +BEGIN + SET NOCOUNT ON; + + DECLARE @Subject nvarchar(255); + SET @Subject = N'Daily SQL Job Failures - ' + @@SERVERNAME; + + IF EXISTS + ( + SELECT 1 + FROM msdb.dbo.sysjobhistory h + WHERE h.step_id = 0 + AND h.run_status = 0 + AND msdb.dbo.agent_datetime(h.run_date, h.run_time) >= DATEADD(DAY, -1, GETDATE()) + ) + BEGIN + EXEC msdb.dbo.sp_send_dbmail + @profile_name = 'SQLAlerts', + @recipients = 'you@domain.com', + @subject = @Subject, + @body = 'Failed SQL Agent jobs in the last 24 hours:', + @query = N' +SET NOCOUNT ON; + +SELECT + @@SERVERNAME AS ServerName, + j.name AS JobName, + msdb.dbo.agent_datetime(h.run_date, h.run_time) AS FailedAt, + STUFF(STUFF(RIGHT(''000000'' + CAST(h.run_duration AS varchar(6)), 6), 3, 0, '':''), + 6, 0, '':'') AS RunDuration_HHMMSS, + h.message AS FailureReason +FROM msdb.dbo.sysjobhistory h +JOIN msdb.dbo.sysjobs j + ON j.job_id = h.job_id +WHERE + h.step_id = 0 + AND h.run_status = 0 + AND msdb.dbo.agent_datetime(h.run_date, h.run_time) >= DATEADD(DAY, -1, GETDATE()) +ORDER BY FailedAt DESC;', + @execute_query_database = 'msdb', + @query_result_header = 1, + @query_result_separator = ' | ', + @query_result_no_padding = 1; + END + ELSE + BEGIN + EXEC msdb.dbo.sp_send_dbmail + @profile_name = 'SQLAlerts', + @recipients = 'you@domain.com', + @subject = @Subject, + @body = 'No SQL Agent job failures were recorded in the last 24 hours.'; + END +END +GO +``` + +--- + +## Step 5: Test the stored procedure manually + +```sql +EXEC msdb.dbo.usp_EmailDailyFailedJobs; +``` + +If this does not send mail, do not create the scheduled job yet. + +--- + +## Step 6: Create the SQL Agent job scheduled for 11:59 PM daily + +```sql +USE msdb; +GO + +DECLARE @job_id UNIQUEIDENTIFIER; + +-- Create the job +EXEC msdb.dbo.sp_add_job + @job_name = N'Daily SQL Job Failure Email', + @enabled = 1, + @description = N'Sends a daily email at 11:59 PM listing failed SQL Agent jobs from the last 24 hours.', + @owner_login_name = N'sa', + @job_id = @job_id OUTPUT; + +-- Add the job step +EXEC msdb.dbo.sp_add_jobstep + @job_id = @job_id, + @step_name = N'Run Failure Report', + @subsystem = N'TSQL', + @database_name = N'msdb', + @command = N'EXEC msdb.dbo.usp_EmailDailyFailedJobs;', + @on_success_action = 1, + @on_fail_action = 2; + +-- Create the schedule +EXEC msdb.dbo.sp_add_schedule + @schedule_name = N'Daily 11:59 PM', + @enabled = 1, + @freq_type = 4, + @freq_interval = 1, + @active_start_time = 235900; + +-- Attach schedule to job +EXEC msdb.dbo.sp_attach_schedule + @job_id = @job_id, + @schedule_name = N'Daily 11:59 PM'; + +-- Attach job to local server +EXEC msdb.dbo.sp_add_jobserver + @job_id = @job_id, + @server_name = N'(LOCAL)'; +GO +``` + +--- + +## Step 7: Test the SQL Agent job immediately + +Do not wait until midnight to find out it is broken. + +```sql +EXEC msdb.dbo.sp_start_job + @job_name = 'Daily SQL Job Failure Email'; +``` + +--- + +## Step 8: Check job history + +```sql +EXEC msdb.dbo.sp_help_jobhistory + @job_name = 'Daily SQL Job Failure Email'; +``` + +--- + +## Helpful verification queries + +### Show mail profiles + +```sql +SELECT name, description +FROM msdb.dbo.sysmail_profile; +``` + +### Show which account is tied to which profile + +```sql +SELECT + p.name AS ProfileName, + a.name AS AccountName, + a.email_address, + a.display_name, + s.servername, + s.port +FROM msdb.dbo.sysmail_profile p +JOIN msdb.dbo.sysmail_profileaccount pa + ON p.profile_id = pa.profile_id +JOIN msdb.dbo.sysmail_account a + ON pa.account_id = a.account_id +JOIN msdb.dbo.sysmail_server s + ON a.account_id = s.account_id; +``` + +### Show public/default profiles + +```sql +SELECT + p.name, + pp.is_default, + pp.principal_id +FROM msdb.dbo.sysmail_principalprofile pp +JOIN msdb.dbo.sysmail_profile p + ON pp.profile_id = p.profile_id; +``` + +### Show mail log/errors + +```sql +SELECT * +FROM msdb.dbo.sysmail_event_log +ORDER BY log_date DESC; +``` + +### Show unsent/sent/failed messages + +```sql +SELECT * +FROM msdb.dbo.sysmail_allitems +ORDER BY send_request_date DESC; +``` + +--- + +## Common failure points + +### 1. `sp_send_dbmail` fails +Usually one of these: + +- wrong profile name +- bad SMTP server/port +- auth failure +- relay denied +- TLS/SSL mismatch +- firewall blocking outbound SMTP + +### 2. Job never runs +Usually one of these: + +- SQL Server Agent service is stopped +- schedule was not attached +- job is disabled + +### 3. Stored procedure compiles but sends nothing +Usually one of these: + +- there were no failed jobs in the last 24 hours +- recipient email is wrong +- mail profile is wrong + +--- + +## Blunt recommendation + +Build and test in this order: + +1. enable Database Mail XPs +2. create account/profile +3. test `sp_send_dbmail` +4. create stored procedure +5. run stored procedure manually +6. create Agent job/schedule +7. manually start Agent job +8. trust the 11:59 PM schedule only after the manual test works + +If step 3 fails, everything after it is pointless until SMTP is fixed. + + +[[!tag notes mssql]] diff --git a/content/posts/wireguard.md b/content/posts/wireguard.md new file mode 100644 index 0000000..cce8d94 --- /dev/null +++ b/content/posts/wireguard.md @@ -0,0 +1,382 @@ +--- +title: "WireGuard VPN Setup on Debian 13" +date: 2026-03-29 +toc: true +tags: ["wireguard","debian","linux"] +--- +Clean, minimal, self-hosted WireGuard setup with: + +- Full tunnel support +- NAT for internet access +- Multi-user capability +- Per-user access control via firewall + +## 0. Assumptions + +- Public interface: `eth0` +- VPN subnet: `10.10.10.0/24` +- Server is publicly reachable + +Replace `eth0` with your real interface name if needed, such as `ens18`, `ens3`, or `enp1s0`. + +## 1. Install WireGuard + +``` +apt update +apt install wireguard +``` + +## 2. Enable IP Forwarding + +``` +sysctl -w net.ipv4.ip_forward=1 +``` + +Persist it: + +``` +echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf +sysctl -p +``` + +## 3. Generate Keys + +``` +cd /etc/wireguard + +wg genkey | tee server.key | wg pubkey > server.pub +wg genkey | tee client.key | wg pubkey > client.pub + +chmod 600 *.key +``` + +## 4. Server Configuration + +Create: + +``` +nano /etc/wireguard/wg0.conf +``` + +``` +[Interface] +Address = 10.10.10.1/24 +PrivateKey = +ListenPort = 51820 + +# NAT + forwarding +PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE +PostUp = iptables -A FORWARD -i wg0 -j ACCEPT +PostUp = iptables -A FORWARD -o wg0 -j ACCEPT + +PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE +PostDown = iptables -D FORWARD -i wg0 -j ACCEPT +PostDown = iptables -D FORWARD -o wg0 -j ACCEPT + +[Peer] +PublicKey = +AllowedIPs = 10.10.10.2/32 +``` + +Notes: + +- `Address = 10.10.10.1/24` is the server's VPN IP. +- `AllowedIPs = 10.10.10.2/32` means that client owns only `10.10.10.2`. +- Do not reuse keys between devices. + +## 5. Start WireGuard + +``` +wg-quick up wg0 +systemctl enable wg-quick@wg0 +``` + +Check status: + +``` +wg show +ip addr show wg0 +``` + +## 6. Client Configuration + +Use this on Windows, Linux, or macOS: + +``` +[Interface] +Address = 10.10.10.2/24 +PrivateKey = +DNS = 1.1.1.1 + +[Peer] +PublicKey = +Endpoint = YOUR_SERVER_IP:51820 +AllowedIPs = 0.0.0.0/0 +PersistentKeepalive = 25 +``` + +Notes: + +- `AllowedIPs = 0.0.0.0/0` sends all client traffic through the VPN. + +- If you only want access to specific private networks, replace `0.0.0.0/0` with those subnets. + +Example split tunnel: + +``` +AllowedIPs = 10.10.10.0/24, 192.168.50.0/24 +``` + +## 7. Open Firewall Port + +If using UFW: + +``` +ufw allow 51820/udp +``` + +Or raw iptables: + +``` +iptables -A INPUT -p udp --dport 51820 -j ACCEPT +``` + +## 8. Test Connection + +From the client: + +``` +curl ip.me +``` + +Expected result: + +- If full tunnel is enabled, this should show the server's public IP. + +Also test: + +``` +ping 10.10.10.1 +``` + +## 9. Add Additional Users + +Generate new keys: + +``` +wg genkey | tee user2.key | wg pubkey > user2.pub +chmod 600 user2.key +``` + +Add to server config: + +``` +[Peer] +PublicKey = +AllowedIPs = 10.10.10.3/32 +``` + +Apply changes: + +``` +wg syncconf wg0 <(wg-quick strip wg0) +``` + +Client config example for user2: + +``` +[Interface] +Address = 10.10.10.3/24 +PrivateKey = +DNS = 1.1.1.1 + +[Peer] +PublicKey = +Endpoint = YOUR_SERVER_IP:51820 +AllowedIPs = 0.0.0.0/0 +PersistentKeepalive = 25 +``` + +## 10. Access Control and Network Design + +WireGuard handles authentication by key. + +WireGuard does **not** decide what a connected peer is allowed to reach. +That is handled by your firewall, typically with `iptables` or `nftables`. + +Think of it like this: + +- WireGuard key + peer IP = identity +- Firewall rules = authorization + +### Important + +The examples below use: + +- `10.10.10.2` = full access user +- `10.10.10.3` = restricted user +- `10.10.10.4` = internet-only user + +Adjust these to your actual peer IP assignments. + +### Scenario A: Public server only, no private networks behind it + +This is the simplest case. + +The server only has: + +- `eth0` facing the internet +- `wg0` for VPN clients + +There is no LAN behind the server. You just want clients to: + +- connect to the server +- route internet traffic through it +- maybe reach services running on the server itself + +#### Full tunnel user + +``` +iptables -A FORWARD -i wg0 -s 10.10.10.2 -o eth0 -j ACCEPT +iptables -A FORWARD -o wg0 -d 10.10.10.2 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +``` + +#### Internet-only restricted user + +``` +iptables -A FORWARD -i wg0 -s 10.10.10.4 -o eth0 -j ACCEPT +iptables -A FORWARD -o wg0 -d 10.10.10.4 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +iptables -A FORWARD -i wg0 -s 10.10.10.4 -j DROP +``` + +That last rule says: + +- allow that peer out to the internet +- drop any other forwarded access attempt + +If the user also needs to reach services hosted directly on the VPN server itself, that is controlled by `INPUT` rules, not `FORWARD`. + +Example: allow SSH from one VPN peer to the server: + +``` +iptables -A INPUT -i wg0 -s 10.10.10.3 -p tcp --dport 22 -j ACCEPT +``` + +Example: block all other VPN clients from SSH to the server: + +``` +iptables -A INPUT -i wg0 -p tcp --dport 22 -j DROP +``` + +### Scenario B: Server has private networks behind it + +Use this if the Debian server can route to private networks such as: + +- `192.168.1.0/24` +- `10.0.0.0/24` +- `172.16.50.0/24` + +In this case, your VPN clients can be selectively allowed to those networks. + +#### Full access user to all routed private networks + +``` +iptables -A FORWARD -i wg0 -s 10.10.10.2 -j ACCEPT +``` + +That is broad. It allows that peer to anything the server can route to. + +#### Restricted user to a single host + +Example: only allow `10.10.10.3` to reach `192.168.1.50` + +``` +iptables -A FORWARD -i wg0 -s 10.10.10.3 -d 192.168.1.50 -j ACCEPT +iptables -A FORWARD -i wg0 -s 10.10.10.3 -j DROP +``` + +#### Restricted user to a whole subnet + +Example: only allow `10.10.10.3` to reach `192.168.1.0/24` + +``` +iptables -A FORWARD -i wg0 -s 10.10.10.3 -d 192.168.1.0/24 -j ACCEPT +iptables -A FORWARD -i wg0 -s 10.10.10.3 -j DROP +``` + +#### Internet plus one private subnet + +Example: allow `10.10.10.3` to: + +- use the server as an internet gateway +- access `192.168.1.0/24` +- nothing else + +``` +iptables -A FORWARD -i wg0 -s 10.10.10.3 -d 192.168.1.0/24 -j ACCEPT +iptables -A FORWARD -i wg0 -s 10.10.10.3 -o eth0 -j ACCEPT +iptables -A FORWARD -o wg0 -d 10.10.10.3 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +iptables -A FORWARD -i wg0 -s 10.10.10.3 -j DROP +``` + +#### Client-side route restriction + +You should also tighten the client config. + +If a peer should only reach one private subnet, do not send all traffic through the tunnel. + +Example client config: + +``` +AllowedIPs = 10.10.10.0/24, 192.168.1.0/24 +``` + +That gives split-tunnel access only to: + +- the VPN subnet +- the private subnet behind the server + +## 11. Remove or Revoke Access + +Remove a peer immediately: + +``` +wg set wg0 peer remove +``` + +Or edit `wg0.conf` and reload: + +``` +wg syncconf wg0 <(wg-quick strip wg0) +``` + +## 12. Common Issues + +- IP forwarding not enabled +- Wrong public interface name +- Firewall blocking UDP 51820 +- NAT rule missing +- Server not publicly reachable +- Client `AllowedIPs` too broad or too narrow +- Client clock wildly wrong +- Forgot `PersistentKeepalive = 25` for clients behind NAT + +## 13. Key Concepts + +- Each peer should have its own keypair +- No passwords or usernames in WireGuard +- One device should not share another device's key +- VPN peer IPs should be treated like identities +- Firewall rules decide what each peer can reach + +## 14. Recommended Next Step + +This walkthrough uses `iptables` because it is straightforward and familiar. + +For a cleaner long-term setup on Debian 13, consider moving the policy to `nftables`, especially if you will have: + +- multiple users +- different access classes +- persistent firewall rules you want managed sanely + +[[!tag wireguard debian linux vpn]] diff --git a/content/posts/wireguard.mdwn b/content/posts/wireguard.mdwn deleted file mode 100644 index 57a25fc..0000000 --- a/content/posts/wireguard.mdwn +++ /dev/null @@ -1,412 +0,0 @@ -# WireGuard VPN Setup on Debian 13 - -Clean, minimal, self-hosted WireGuard setup with: - -- Full tunnel support -- NAT for internet access -- Multi-user capability -- Per-user access control via firewall - ---- - -## 0. Assumptions - -- Public interface: `eth0` -- VPN subnet: `10.10.10.0/24` -- Server is publicly reachable - -Replace `eth0` with your real interface name if needed, such as `ens18`, `ens3`, or `enp1s0`. - ---- - -## 1. Install WireGuard - -``` -apt update -apt install wireguard -``` - ---- - -## 2. Enable IP Forwarding - -``` -sysctl -w net.ipv4.ip_forward=1 -``` - -Persist it: - -``` -echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf -sysctl -p -``` - ---- - -## 3. Generate Keys - -``` -cd /etc/wireguard - -wg genkey | tee server.key | wg pubkey > server.pub -wg genkey | tee client.key | wg pubkey > client.pub - -chmod 600 *.key -``` - ---- - -## 4. Server Configuration - -Create: - -``` -nano /etc/wireguard/wg0.conf -``` - -``` -[Interface] -Address = 10.10.10.1/24 -PrivateKey = -ListenPort = 51820 - -# NAT + forwarding -PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE -PostUp = iptables -A FORWARD -i wg0 -j ACCEPT -PostUp = iptables -A FORWARD -o wg0 -j ACCEPT - -PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE -PostDown = iptables -D FORWARD -i wg0 -j ACCEPT -PostDown = iptables -D FORWARD -o wg0 -j ACCEPT - -[Peer] -PublicKey = -AllowedIPs = 10.10.10.2/32 -``` - -Notes: - -- `Address = 10.10.10.1/24` is the server's VPN IP. -- `AllowedIPs = 10.10.10.2/32` means that client owns only `10.10.10.2`. -- Do not reuse keys between devices. - ---- - -## 5. Start WireGuard - -``` -wg-quick up wg0 -systemctl enable wg-quick@wg0 -``` - -Check status: - -``` -wg show -ip addr show wg0 -``` - ---- - -## 6. Client Configuration - -Use this on Windows, Linux, or macOS: - -``` -[Interface] -Address = 10.10.10.2/24 -PrivateKey = -DNS = 1.1.1.1 - -[Peer] -PublicKey = -Endpoint = YOUR_SERVER_IP:51820 -AllowedIPs = 0.0.0.0/0 -PersistentKeepalive = 25 -``` - -Notes: - -- `AllowedIPs = 0.0.0.0/0` sends all client traffic through the VPN. - -- If you only want access to specific private networks, replace `0.0.0.0/0` with those subnets. - -Example split tunnel: - -``` -AllowedIPs = 10.10.10.0/24, 192.168.50.0/24 -``` - ---- - -## 7. Open Firewall Port - -If using UFW: - -``` -ufw allow 51820/udp -``` - -Or raw iptables: - -``` -iptables -A INPUT -p udp --dport 51820 -j ACCEPT -``` - ---- - -## 8. Test Connection - -From the client: - -``` -curl ip.me -``` - -Expected result: - -- If full tunnel is enabled, this should show the server's public IP. - -Also test: - -``` -ping 10.10.10.1 -``` - ---- - -## 9. Add Additional Users - -Generate new keys: - -``` -wg genkey | tee user2.key | wg pubkey > user2.pub -chmod 600 user2.key -``` - -Add to server config: - -``` -[Peer] -PublicKey = -AllowedIPs = 10.10.10.3/32 -``` - -Apply changes: - -``` -wg syncconf wg0 <(wg-quick strip wg0) -``` - -Client config example for user2: - -``` -[Interface] -Address = 10.10.10.3/24 -PrivateKey = -DNS = 1.1.1.1 - -[Peer] -PublicKey = -Endpoint = YOUR_SERVER_IP:51820 -AllowedIPs = 0.0.0.0/0 -PersistentKeepalive = 25 -``` - ---- - -## 10. Access Control and Network Design - -WireGuard handles authentication by key. - -WireGuard does **not** decide what a connected peer is allowed to reach. -That is handled by your firewall, typically with `iptables` or `nftables`. - -Think of it like this: - -- WireGuard key + peer IP = identity -- Firewall rules = authorization - -### Important - -The examples below use: - -- `10.10.10.2` = full access user -- `10.10.10.3` = restricted user -- `10.10.10.4` = internet-only user - -Adjust these to your actual peer IP assignments. - ---- - -### Scenario A: Public server only, no private networks behind it - -This is the simplest case. - -The server only has: - -- `eth0` facing the internet -- `wg0` for VPN clients - -There is no LAN behind the server. You just want clients to: - -- connect to the server -- route internet traffic through it -- maybe reach services running on the server itself - -#### Full tunnel user - -``` -iptables -A FORWARD -i wg0 -s 10.10.10.2 -o eth0 -j ACCEPT -iptables -A FORWARD -o wg0 -d 10.10.10.2 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -``` - -#### Internet-only restricted user - -``` -iptables -A FORWARD -i wg0 -s 10.10.10.4 -o eth0 -j ACCEPT -iptables -A FORWARD -o wg0 -d 10.10.10.4 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -iptables -A FORWARD -i wg0 -s 10.10.10.4 -j DROP -``` - -That last rule says: - -- allow that peer out to the internet -- drop any other forwarded access attempt - -If the user also needs to reach services hosted directly on the VPN server itself, that is controlled by `INPUT` rules, not `FORWARD`. - -Example: allow SSH from one VPN peer to the server: - -``` -iptables -A INPUT -i wg0 -s 10.10.10.3 -p tcp --dport 22 -j ACCEPT -``` - -Example: block all other VPN clients from SSH to the server: - -``` -iptables -A INPUT -i wg0 -p tcp --dport 22 -j DROP -``` - ---- - -### Scenario B: Server has private networks behind it - -Use this if the Debian server can route to private networks such as: - -- `192.168.1.0/24` -- `10.0.0.0/24` -- `172.16.50.0/24` - -In this case, your VPN clients can be selectively allowed to those networks. - -#### Full access user to all routed private networks - -``` -iptables -A FORWARD -i wg0 -s 10.10.10.2 -j ACCEPT -``` - -That is broad. It allows that peer to anything the server can route to. - -#### Restricted user to a single host - -Example: only allow `10.10.10.3` to reach `192.168.1.50` - -``` -iptables -A FORWARD -i wg0 -s 10.10.10.3 -d 192.168.1.50 -j ACCEPT -iptables -A FORWARD -i wg0 -s 10.10.10.3 -j DROP -``` - -#### Restricted user to a whole subnet - -Example: only allow `10.10.10.3` to reach `192.168.1.0/24` - -``` -iptables -A FORWARD -i wg0 -s 10.10.10.3 -d 192.168.1.0/24 -j ACCEPT -iptables -A FORWARD -i wg0 -s 10.10.10.3 -j DROP -``` - -#### Internet plus one private subnet - -Example: allow `10.10.10.3` to: - -- use the server as an internet gateway -- access `192.168.1.0/24` -- nothing else - -``` -iptables -A FORWARD -i wg0 -s 10.10.10.3 -d 192.168.1.0/24 -j ACCEPT -iptables -A FORWARD -i wg0 -s 10.10.10.3 -o eth0 -j ACCEPT -iptables -A FORWARD -o wg0 -d 10.10.10.3 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -iptables -A FORWARD -i wg0 -s 10.10.10.3 -j DROP -``` - -#### Client-side route restriction - -You should also tighten the client config. - -If a peer should only reach one private subnet, do not send all traffic through the tunnel. - -Example client config: - -``` -AllowedIPs = 10.10.10.0/24, 192.168.1.0/24 -``` - -That gives split-tunnel access only to: - -- the VPN subnet -- the private subnet behind the server - ---- - -## 11. Remove or Revoke Access - -Remove a peer immediately: - -``` -wg set wg0 peer remove -``` - -Or edit `wg0.conf` and reload: - -``` -wg syncconf wg0 <(wg-quick strip wg0) -``` - ---- - -## 12. Common Issues - -- IP forwarding not enabled -- Wrong public interface name -- Firewall blocking UDP 51820 -- NAT rule missing -- Server not publicly reachable -- Client `AllowedIPs` too broad or too narrow -- Client clock wildly wrong -- Forgot `PersistentKeepalive = 25` for clients behind NAT - ---- - -## 13. Key Concepts - -- Each peer should have its own keypair -- No passwords or usernames in WireGuard -- One device should not share another device's key -- VPN peer IPs should be treated like identities -- Firewall rules decide what each peer can reach - ---- - -## 14. Recommended Next Step - -This walkthrough uses `iptables` because it is straightforward and familiar. - -For a cleaner long-term setup on Debian 13, consider moving the policy to `nftables`, especially if you will have: - -- multiple users -- different access classes -- persistent firewall rules you want managed sanely - -[[!tag wireguard debian linux vpn]]