Archive
Building a Wildcard Catch-All POP3 Mail Server on Ubuntu
Receive mail for any address on any subdomain — no per-account configuration required
Introduction
This guide walks through setting up a wildcard catch-all mail server on Ubuntu using Postfix and Dovecot. The goal is to receive email sent to any address on any subdomain of your domain — for example, anything@abc.yourdomain.com or test@xyz.yourdomain.com — without having to configure individual mailboxes in advance.
This is particularly useful for testing, disposable address systems, API integrations, and mail sink setups where you want to capture inbound mail programmatically. The server will not send mail — only receive it. Mail older than 24 hours is automatically purged.
Architecture Overview
The stack consists of three components working together:
- Postfix — receives inbound SMTP and delivers to a local virtual mailbox
- Dovecot — serves POP3 access to the mailbox
- A single catch-all mailbox — all mail for all subdomains and addresses funnels into one Maildir
Rather than creating individual accounts, everything is routed to a single mailbox. A POP3 client connects with one username and password to retrieve all mail regardless of which address or subdomain it was sent to.
Part 1 — DNS Configuration
How Wildcard MX Records Work
MX records must point to a hostname, not an IP address directly. This means two DNS records are needed: an MX record pointing to a mail hostname, and an A record resolving that hostname to your server’s IP address.
Create the following records in your DNS provider (AWS Route 53 or equivalent):
| Record Name | Type / Value |
| *.yourdomain.com | MX — 10 mail.yourdomain.com |
| mail.yourdomain.com | A — your.server.ip.address |
The wildcard MX record *.yourdomain.com matches any single-level subdomain lookup. When a sending mail server looks up the MX record for abc.yourdomain.com, it matches the wildcard and is directed to mail.yourdomain.com, which in turn resolves to your server’s IP via the A record.
Note that the wildcard covers one subdomain level deep. Mail to anything@abc.yourdomain.com is covered. A deeper level such as anything@a.b.yourdomain.com would require a separate record.
Verifying DNS Records
From a Windows machine, use nslookup to verify records have propagated:
| # Check the MX recordnslookup -type=MX abc.yourdomain.com # Check the A record for the mail hostnslookup mail.yourdomain.com # Query AWS nameservers directly (before public propagation)nslookup -type=NS yourdomain.comnslookup -type=MX abc.yourdomain.com ns-123.awsdns-45.com |
You can also use dnschecker.org to check propagation across multiple global resolvers simultaneously.
Part 2 — Server Setup
Install Postfix and Dovecot
| sudo apt updatesudo apt install postfix dovecot-pop3d -y |
During the Postfix installation prompt, select Internet Site and enter your domain name (e.g. yourdomain.com) when asked for the mail name.
Configure Postfix
Edit the main Postfix configuration file:
| sudo nano /etc/postfix/main.cf |
Add or update the following values:
| myhostname = mail.yourdomain.commydomain = yourdomain.com # Leave mydestination empty — we use virtual mailboxes insteadmydestination = # Accept mail for any subdomain matching the wildcardvirtual_mailbox_domains = regexp:/etc/postfix/virtual_domainsvirtual_mailbox_base = /var/mail/vhostsvirtual_mailbox_maps = regexp:/etc/postfix/virtual_mailboxvirtual_minimum_uid = 100virtual_uid_maps = static:5000virtual_gid_maps = static:5000 # Required to prevent open relaysmtpd_relay_restrictions = permit_mynetworks, reject_unauth_destination |
Create the virtual domains file — this regexp matches any subdomain of your domain:
| sudo nano /etc/postfix/virtual_domains |
| /^\.+\.yourdomain\.com$/ OK |
Create the virtual mailbox map — this catches all addresses and routes them to a single catchall mailbox:
| sudo nano /etc/postfix/virtual_mailbox |
| /^.+@.+\.yourdomain\.com$/ catchall/ |
Rebuild the aliases database (required to avoid a startup warning):
| newaliases |
Create the Virtual Mail User and Mailbox
Postfix delivers mail as a dedicated system user (vmail). Create the user, group, and mailbox directory:
| sudo groupadd -g 5000 vmailsudo useradd -u 5000 -g 5000 -d /var/mail/vhosts -s /sbin/nologin vmailsudo mkdir -p /var/mail/vhosts/catchallsudo chown -R vmail:vmail /var/mail/vhosts |
Configure Dovecot for POP3
Enable the POP3 protocol in the main Dovecot config:
| sudo nano /etc/dovecot/dovecot.conf |
| protocols = pop3 |
Set the mail location to the catchall Maildir:
| sudo nano /etc/dovecot/conf.d/10-mail.conf |
| mail_location = maildir:/var/mail/vhosts/catchall |
Allow plaintext authentication (suitable for internal/trusted use — see the TLS note at the end for public-facing deployments):
| sudo nano /etc/dovecot/conf.d/10-auth.conf |
| disable_plaintext_auth = noauth_mechanisms = plain login passdb { driver = passwd-file args = /etc/dovecot/users} userdb { driver = static args = uid=5000 gid=5000 home=/var/mail/vhosts/catchall} |
Create the Dovecot users file with your chosen credentials:
| sudo nano /etc/dovecot/users # Format: username:{PLAIN}passwordmailuser:{PLAIN}yourpasswordhere |
Start the Services
| sudo systemctl restart postfixsudo systemctl restart dovecot |
Verify Postfix is running:
| postfix status |
Check the mail log for any errors:
| tail -30 /var/log/mail.log |
Part 3 — Firewall Configuration
Cloud Firewall (Linode / AWS / equivalent)
Open the following inbound ports in your cloud provider’s firewall. On Linode this is found under Networking > Firewalls in the dashboard. Changes apply immediately with no reboot required.
| Port / Protocol | Purpose |
| 22 TCP | SSH (ensure this is always open) |
| 25 TCP | SMTP inbound (receiving mail) |
| 110 TCP | POP3 (retrieving mail) |
UFW on the Ubuntu Instance
| sudo ufw allow 22/tcpsudo ufw allow 25/tcpsudo ufw allow 110/tcpsudo ufw enablesudo ufw status |
Always confirm port 22 is allowed before enabling UFW to avoid locking yourself out of SSH.
Part 4 — Testing
Test SMTP Locally
From the server itself, connect to Postfix on port 25 and send a test message. Use 127.0.0.1 rather than localhost to avoid IPv6 connection issues:
| telnet 127.0.0.1 25 |
You should immediately see the greeting banner:
| 220 mail.yourdomain.com ESMTP Postfix |
Then send a test message interactively:
| EHLO test.comMAIL FROM:<test@test.com>RCPT TO:<anything@abc.yourdomain.com>DATASubject: Test mail Hello this is a test.QUIT |
Each step should return a 250 OK response. The RCPT TO line is the critical one — if the wildcard regexp is configured correctly, Postfix will accept any subdomain address. After QUIT, verify the mail landed in the mailbox:
| tail -20 /var/log/mail.logls -la /var/mail/vhosts/catchall/new/ |
You should see a file in the new/ directory — that is the email in Maildir format.
Test POP3 Locally
| telnet 127.0.0.1 110 |
Dovecot should respond with:
| +OK Dovecot (Ubuntu) ready. |
Then authenticate and list messages:
| USER mailuserPASS yourpasswordhereLISTRETR 1QUIT |
A successful LIST response showing message count confirms the full chain is working: inbound SMTP via Postfix, delivery to virtual Maildir, and POP3 retrieval via Dovecot.
Part 5 — Automatic Mail Purge
To automatically delete mail older than 24 hours, add a cron job:
| sudo crontab -e |
Add the following line:
| 0 * * * * find /var/mail/vhosts/catchall -type f -mmin +1440 -delete |
This runs every hour and removes any file in the catchall mailbox older than 1440 minutes (24 hours).
Optional — Silence the Backwards Compatibility Warning
Postfix logs a harmless warning about backwards-compatible default settings. To silence it:
| postconf compatibility_level=3.6postfix reload |
Security Notes
- Port 110 transmits credentials in plaintext. For any public-facing deployment, configure Dovecot with TLS and use POP3S on port 995 instead.
- The smtpd_relay_restrictions = permit_mynetworks, reject_unauth_destination setting prevents your server from acting as an open relay — do not remove this.
- Consider rate limiting inbound SMTP connections if the server is publicly accessible to reduce spam load.
- The vmail system user has no login shell (nologin) and cannot be used to access the system interactively.
Summary
With Postfix and Dovecot configured as described above, your server will:
- Accept inbound SMTP for any address on any subdomain of your domain
- Deliver all mail into a single catch-all Maildir with no per-account configuration
- Expose all received mail via POP3 using a single username and password
- Automatically purge mail older than 24 hours
- Require no restart or reconfiguration when new subdomains or addresses are used
How to Detect and Fix Squid Proxy Abuse
Running an open HTTP proxy on the internet, even temporarily for testing, can quickly attract unwanted attention. Within minutes of deploying an unsecured Squid proxy, malicious actors can discover and abuse it for scanning, attacks, or hiding their origin. Here’s how to spot the warning signs and lock down your proxy.
Symptoms of Proxy Abuse
1. Proxy Stops Working
The most obvious symptom is that your proxy simply stops responding to legitimate requests. Connections timeout or hang indefinitely, even though the Squid service appears to be running.
2. Cache File Descriptor Warnings
When checking the Squid service status, you see repeated warnings like:
WARNING: Your cache is running out of file descriptors
This occurs because the proxy is handling far more concurrent connections than expected for a small test server.
3. Service Shows Active But Unresponsive
The systemd status shows Squid as “active (running)” with normal startup messages, but actual proxy requests fail:
bash
$ sudo systemctl status squid● squid.service - Squid Web Proxy Server Active: active (running)
Yet when you try to use it:
bash
$ curl -x http://your-proxy:8888 https://example.comcurl: (28) Failed to connect to example.com port 443 after 21060ms
4. High Memory or CPU Usage
A small EC2 instance (t2.micro or t3.micro) that should be mostly idle shows elevated resource consumption.
How to Verify Proxy Abuse
Check the Access Logs
The quickest way to confirm abuse is to examine the Squid access log:
bash
sudo tail -100 /var/log/squid/access.log
What to look for:
A healthy proxy used only by you should show:
- One or two source IP addresses (yours)
- Requests to legitimate domains
- Occasional HTTPS CONNECT requests
An abused proxy will show:
- Dozens of different source IP addresses
- CONNECT requests to random IP addresses (not domain names)
- Strange ports: SSH (22), Telnet (23), email ports (25, 587, 993, 110), random high ports
- High frequency of requests
Example of Abuse
Here’s what an abused proxy log looks like:
1770200370.089 59842 172.234.115.25 TCP_TUNNEL/503 0 CONNECT 188.64.128.123:221770200370.089 59842 51.83.10.33 TCP_TUNNEL/503 0 CONNECT 188.64.132.53:4431770200370.089 59841 172.234.115.25 TCP_TUNNEL/503 0 CONNECT 188.64.128.4:221770200370.089 59332 185.90.61.84 TCP_TUNNEL/503 0 CONNECT 188.64.129.251:80001770200370.089 214 91.202.74.22 TCP_TUNNEL/503 0 CONNECT 188.64.129.143:231770200370.191 579 51.83.10.33 TCP_TUNNEL/200 39 CONNECT 188.64.128.4:80211770200370.235 11227 51.83.10.33 TCP_TUNNEL/200 176 CONNECT 188.64.131.66:587
Notice:
- Multiple unique source IPs
- Connections to SSH (port 22), Telnet (port 23), SMTP (port 587)
- Targets are raw IP addresses, not domain names
- Hundreds of requests per minute
This is classic behavior of attackers using your proxy to scan the internet for vulnerable services.
Count Unique IPs
To see how many different IPs are using your proxy:
bash
sudo awk '{print $3}' /var/log/squid/access.log | sort | uniq -c | sort -rn | head -20
If you see more than a handful of IPs (especially if you’re the only legitimate user), your proxy is being abused.
Check Current Connections
See active connections to your proxy port:
bash
sudo netstat -tn | grep :8888
A legitimate test proxy should have 0-2 active connections. Dozens of connections indicate abuse.
How to Fix It Immediately
1. Lock Down the AWS Security Group
The fastest fix is to restrict access at the network level:
Via AWS Console:
- Navigate to EC2 → Security Groups
- Select the security group attached to your proxy instance
- Click “Edit inbound rules”
- Find the rule for your proxy port (e.g., 8888)
- Change Source from
0.0.0.0/0to “My IP”- AWS will auto-detect and fill in your current public IP
- Click “Save rules”
The change takes effect immediately – no restart required.
2. Restart Squid to Kill Existing Connections
Even after locking down the security group, existing connections may persist:
bash
sudo systemctl restart squid
3. Clear the Logs
Start fresh to verify the abuse has stopped:
bash
# Stop Squidsudo systemctl stop squid# Clear logssudo truncate -s 0 /var/log/squid/access.logsudo truncate -s 0 /var/log/squid/cache.log# Clear cache if you're seeing file descriptor warningssudo rm -rf /var/spool/squid/*sudo squid -z# Restartsudo systemctl start squid
4. Verify It’s Fixed
Watch the log in real-time:
bash
sudo tail -f /var/log/squid/access.log
If tail -f just sits there with no output, that’s good – it means no requests are coming through.
Now test from your own machine:
bash
curl -x http://your-proxy-ip:8888 https://ifconfig.me
You should immediately see your request appear in the log, and nothing else.
Prevention Best Practices
For Testing Environments
- Always use IP whitelisting – Never expose a proxy to
0.0.0.0/0even for testing - Use non-standard ports – While not security through obscurity, it reduces automated scanning
- Set up authentication – Even basic auth is better than nothing
- Monitor logs – Check periodically for unexpected traffic
- Terminate when done – Don’t leave test infrastructure running
Minimal Squid Config with Authentication
For slightly better security, add basic authentication:
bash
# Install htpasswdsudo apt install apache2-utils# Create password filesudo htpasswd -c /etc/squid/passwords testuser# Edit squid.confsudo nano /etc/squid/squid.conf
Add these lines:
http_port 8888# Basic authenticationauth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwordsauth_param basic realm proxyacl authenticated proxy_auth REQUIREDhttp_access allow authenticatedhttp_access deny allcache deny all
Restart Squid and now clients must authenticate:
bash
curl -x http://testuser:password@your-proxy:8888 https://example.com
For Production
If you need a production proxy:
- Use a proper reverse proxy like nginx or HAProxy with TLS
- Implement OAuth or certificate-based authentication
- Use AWS PrivateLink or VPC peering instead of public exposure
- Enable detailed logging and monitoring
- Set up rate limiting
- Consider managed solutions like AWS API Gateway or CloudFront
Conclusion
Open proxies are magnets for abuse. Automated scanners continuously sweep the internet looking for misconfigured proxies to exploit. The symptoms are often subtle – file descriptor warnings, poor performance, or timeouts – but the fix is straightforward: restrict access to only trusted IP addresses at the network level.
For testing purposes, AWS Security Groups provide the perfect solution: instant IP-based access control with no performance overhead. Combined with monitoring the Squid access logs, you can quickly detect and eliminate abuse before it impacts your testing or incurs unexpected costs.
Remember: if you’re running a temporary test proxy, lock it down to your IP from the start. It only takes minutes for automated scanners to find and abuse an open proxy.
Key Takeaways:
- ✅ Always restrict proxy access via security groups/firewall rules
- ✅ Monitor access logs for unexpected IP addresses
- ✅ Watch for file descriptor warnings as an early sign of abuse
- ✅ Clear logs and restart after securing to verify the fix
- ✅ Terminate test infrastructure when finished to avoid ongoing costs
How to Extract #EXIF Data from an Image in .NET 8 with #MetadataExtractor

GIT REPO : https://github.com/infiniteloopltd/ExifResearch
When working with images, EXIF (Exchangeable Image File Format) data can provide valuable information such as the camera model, date and time of capture, GPS coordinates, and much more. Whether you’re building an image processing application or simply want to extract metadata for analysis, knowing how to retrieve EXIF data in a .NET environment is essential.
In this post, we’ll walk through how to extract EXIF data from an image in .NET 8 using the cross-platform MetadataExtractor library.
Why Use MetadataExtractor?
.NET’s traditional System.Drawing.Common library has limitations when it comes to cross-platform compatibility, particularly for non-Windows environments. The MetadataExtractor library, however, is a powerful and platform-independent solution for extracting metadata from various image formats, including EXIF data.
With MetadataExtractor, you can read EXIF metadata from images in a clean, efficient way, making it an ideal choice for .NET Core and .NET 8 developers working on cross-platform applications.
Step 1: Install MetadataExtractor
To begin, you need to add the MetadataExtractor NuGet package to your project. You can install it using the following command:
bashCopy codedotnet add package MetadataExtractor
This package supports EXIF, IPTC, XMP, and many other metadata formats from various image file types.
Step 2: Writing the Code to Extract EXIF Data
Now that the package is installed, let’s write some code to extract EXIF data from an image stored as a byte array.
Here is the complete function:
using System;
using System.Collections.Generic;
using System.IO;
using MetadataExtractor;
using MetadataExtractor.Formats.Exif;
public class ExifReader
{
public static Dictionary<string, string> GetExifData(byte[] imageBytes)
{
var exifData = new Dictionary<string, string>();
try
{
using var ms = new MemoryStream(imageBytes);
var directories = ImageMetadataReader.ReadMetadata(ms);
foreach (var directory in directories)
{
foreach (var tag in directory.Tags)
{
// Add tag name and description to the dictionary
exifData[tag.Name] = tag.Description;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error reading EXIF data: {ex.Message}");
}
return exifData;
}
}
How It Works:
- Reading Image Metadata: The function uses
ImageMetadataReader.ReadMetadatato read all the metadata from the byte array containing the image. - Iterating Through Directories and Tags: EXIF data is organized in directories (for example, the main EXIF data, GPS, and thumbnail directories). We iterate through these directories and their associated tags.
- Handling Errors: We wrap the logic in a
try-catchblock to ensure any potential errors (e.g., unsupported formats) are handled gracefully.
Step 3: Usage Example
To use this function, you can pass an image byte array to it. Here’s an example:
using System;
using System.IO;
class Program
{
static void Main()
{
// Replace with your byte array containing an image
byte[] imageBytes = File.ReadAllBytes("example.jpg");
var exifData = ExifReader.GetExifData(imageBytes);
foreach (var kvp in exifData)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
}
}
This code reads an image from the file system as a byte array and then uses the ExifReader.GetExifData method to extract the EXIF data. Finally, it prints out the EXIF tags and their descriptions.
Example Output:
If the image contains EXIF metadata, the output might look something like this:
"Compression Type": "Baseline",
"Data Precision": "8 bits",
"Image Height": "384 pixels",
"Image Width": "512 pixels",
"Number of Components": "3",
"Component 1": "Y component: Quantization table 0, Sampling factors 2 horiz/2 vert",
"Component 2": "Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert",
"Component 3": "Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert",
"Make": "samsung",
"Model": "SM-G998B",
"Orientation": "Right side, top (Rotate 90 CW)",
"X Resolution": "72 dots per inch",
"Y Resolution": "72 dots per inch",
"Resolution Unit": "Inch",
"Software": "G998BXXU7EWCH",
"Date/Time": "2023:05:02 12:33:47",
"YCbCr Positioning": "Center of pixel array",
"Exposure Time": "1/33 sec",
"F-Number": "f/2.2",
"Exposure Program": "Program normal",
"ISO Speed Ratings": "640",
"Exif Version": "2.20",
"Date/Time Original": "2023:05:02 12:33:47",
"Date/Time Digitized": "2023:05:02 12:33:47",
"Time Zone": "+09:00",
"Time Zone Original": "+09:00",
"Shutter Speed Value": "1 sec",
"Aperture Value": "f/2.2",
"Exposure Bias Value": "0 EV",
"Max Aperture Value": "f/2.2",
"Metering Mode": "Center weighted average",
"Flash": "Flash did not fire",
"Focal Length": "2.2 mm",
"Sub-Sec Time": "404",
"Sub-Sec Time Original": "404",
"Sub-Sec Time Digitized": "404",
"Color Space": "sRGB",
"Exif Image Width": "4000 pixels",
"Exif Image Height": "3000 pixels",
"Exposure Mode": "Auto exposure",
"White Balance Mode": "Auto white balance",
"Digital Zoom Ratio": "1",
"Focal Length 35": "13 mm",
"Scene Capture Type": "Standard",
"Unique Image ID": "F12XSNF00NM",
"Compression": "JPEG (old-style)",
"Thumbnail Offset": "824 bytes",
"Thumbnail Length": "49594 bytes",
"Number of Tables": "4 Huffman tables",
"Detected File Type Name": "JPEG",
"Detected File Type Long Name": "Joint Photographic Experts Group",
"Detected MIME Type": "image/jpeg",
"Expected File Name Extension": "jpg"
This is just a small sample of the information EXIF can store. Depending on the camera and settings, you may find data on GPS location, white balance, focal length, and more.
Why Use EXIF Data?
EXIF data can be valuable in various scenarios:
- Image processing: Automatically adjust images based on camera settings (e.g., ISO or exposure time).
- Data analysis: Track when and where photos were taken, especially when handling large datasets of images.
- Digital forensics: Verify image authenticity by analyzing EXIF metadata for manipulation or alterations.
Conclusion
With the MetadataExtractor library, extracting EXIF data from an image is straightforward and cross-platform compatible. Whether you’re building a photo management app, an image processing tool, or just need to analyze metadata, this approach is an efficient solution for working with EXIF data in .NET 8.
By using this solution, you can extract a wide range of metadata from images, making your applications smarter and more capable. Give it a try and unlock the hidden data in your images!