Non-static IP: script to alert change - dns

I have a server at home with basic DSL. I registered a domain name (at GoDaddy) and can login remotely just fine. However, this requires the input of the IP address and a static IP would be prohibitively expensive. I haven't found a good dynamic service. Instead, it seems that my IP address doesn't change very often--maybe once a month--and so it's just as easy for me to update the GoDaddy domain information. However, I would like to receive an alert from my server when this change happens. Here's the script that I wrote to do this:
#!/bin/bash
oldipinfo=""
while [ 1 ]
do
sleep 3600
ipinfo=`lynx -dump checkip.dyndns.org`
if [ ipinfo != oldipinfo ]
then
echo "New IP for server $(ipinfo)" | mail -s "NEW IP!" myaddress#gmail.com
echo $ipinfo >> ipinfo.out
oldipinfo=ipinfo
fi
done
This actually e-mails me (and updates the file) every hour, so I've done something wrong?
Thanks for your help!
JV

You never read the oldipinfo from the file you cache it in, so your script always has oldipinfo set to an empty string, meaning that "" != x.x.x.x will always be true.
You should have
oldipinfo=`cat ipinfo.txt`

Related

How can I catch a SERVFAIL exception using Python's dns resolver?

I'm looking to query a domain like this:
dns.resolver.resolve("dnssec-failed.org","A")
Which returns an error like this:
raise NoNameservers(request=self.request, errors=self.errors)
dns.resolver.NoNameservers: All nameservers failed to answer the query dnssec-failed.org. IN A: Server 127.0.0.1 UDP port 53 answered SERVFAIL
I want to be able to catch that exception in my function like so:
def get_a_record(url):
try:
answers = dns.resolver.resolve(url,"A")
except dns.resolver.SERVFAIL:
print("SERVFAIL error for %s" % url)
except dns.resolver.NXDOMAIN:
print("No such domain %s" % url)
except dns.resolver.Timeout:
print("Timed out while resolving %s" % url)
except dns.exception.DNSException:
print("Unhandled exception")
Now I know in the above snippet dns resolver doesn't have a SERVAIL exception but what I'd like to do is catch the error, be able to log it, and continue my script. Is there a proper way to do this using the dns resolver package, or would I need to call the dig command and parse that result?
EDIT
For clarification, I only used dnssec-failed.org as an example because it results in (what I thought) would be the same response as something I am specifically looking, for but don't actually have any active examples of. That "something" being domains which point to ip addresses that are no longer in use. Dangling NS records in other words.
For example I use an IP address that is loaned to me by AWS for use in some XYZ cloud-based application, and I create the name-to-address mapping records in my DNS zone. If I decide to deprecate this service and return the ip back to the cloud provider's pool of ips but forget to remove the DNS record from the zone, it is left "dangling".
That is what I am looking for and I mistakenly assumed that a SERVFAIL is the type of response I get from a query like dig domain-with-no-ip.com
Apologies for the confusion.
EDIT 2
I went and tested this by taking a domain I'd already registered. Configured an A record for it and pointed it to an Ubuntu EC2 listening on port 7272 (python3 -m http.server 7272). Waited 5 minutes for the zone to propagate and then I was able to reach my domain, publicly. All fine and good.
Then I stopped the instance, waited a bit, and then restarted it. Upon coming back up it had a new public ip. Great. So at this point there is a dangling A record for me to test.
So I do dig and nslookup on the domain. Both come back with perfectly fine answers. They just simply point to the now old/original public ip. And that makes sense since the DNS record hasn't changed. The only observable thing that really changes is something like curl, which times out.
So unless my understanding is still wrong, there really isn't an all-too reliable way to hunt down dangling A records because basing logic off n http timeout doesn't necessarily imply a dangling record. The server could just be off/down and the ip is still attached to the resource. Am I correct in my understanding or am I missing something still?
EDIT 3
Accepting the answer because even though my question mildly evolved into something else, the answer did technically address the original question of my post and I think that warrants accepting it.
First, dnssec-failed.org has nameservers but is, by design, failing DNSSEC.
Hence a simple query towards any recursive nameserver that does DNSSEC validation will fail with SERVFAIL as expected:
$ dig dnssec-failed.org NS +short
(no output)
$ dig dnssec-failed.org NS | grep status: | tail -1
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 46517
but as soon as you disable DNSSEC validation you get the nameservers as it should be:
$ dig dnssec-failed.org NS +cdflag +noall +ans +nottlunits
dnssec-failed.org. 7200 IN NS dns105.comcast.net.
dnssec-failed.org. 7200 IN NS dns101.comcast.net.
dnssec-failed.org. 7200 IN NS dns102.comcast.net.
dnssec-failed.org. 7200 IN NS dns103.comcast.net.
dnssec-failed.org. 7200 IN NS dns104.comcast.net.
Now back to the Python part.
resolve() from dnspython is an high level API call, it does everything a resolver does, that is it potentially recurse from root up to being able to give you an answer. Hence, this simple call hides possibly multiple questions and responses and as such may not expose you to the real underlying problem, but provides high level API in output also, using an exception.
As you can see in your own example, you have the SERVFAIL right in the error message, but it is an NoNameservers exceptions because the code asked the registry nameservers for the list of nameservers (which works, there is a DS for this name in parent nameservers), and then ask for any of those nameservers for further data and then they fail there DNSSEC validation, hence the final exception.
It is not clear to me what is your position on DNSSEC error in your case, if you do not care about them or if you really want to study them and do something particular. Hence the above solutions may need to be adapted. If you do not care, just log the NoNameservers exception and go on, everything will work as excepted, DNSSEC validation error will happen exactly like a broken domain, which is per design.
Hence do you really need to handle DNSSEC errors in any way different from any other errors? Why can't you catch NoNameservers exception, log it, and go further?
Otherwise the quick (and dirty way), just parse the error message attached to the NoNameservers exception, and if you see SERVFAIL you can suppose (but not be 100% sure) it is a DNSSEC problem, and at least go further as you need.
If you really need to have further details and be sure it is a DNSSEC problem, you need to do the equivalent of what is above for dig, that is do 2 queries that just differ in the CD DNS flag, and compare results. Which means going "lower" than resolve() API and use dns.query directly, such as this way:
>>> import dns, dns.rcode
>>> resolver_ip = '8.8.8.8' # Use any recursive **validating** nameserver that you trust
>>> query=dns.message.make_query('dnssec-failed.org', 'A')
>>> response = dns.query.udp_with_fallback(query, resolver_ip)[0]
>>> response.rcode() == dns.rcode.SERVFAIL
True
# Now checking if disabling DNSSEC resolves the problem and gets us a reply
# If so, it really means there is a DNSSEC problem
>>> print(str(query))
id 65008
opcode QUERY
rcode NOERROR
flags RD
;QUESTION
dnssec-failed.org. IN A
;ANSWER
;AUTHORITY
;ADDITIONAL
>>> query.flags
<Flag.RD: 256>
>>> query.flags = query.flags | dns.flags.CD
>>> query.flags
<Flag.RD|CD: 272>
>>> print(str(query))
id 65008
opcode QUERY
rcode NOERROR
flags RD CD
;QUESTION
dnssec-failed.org. IN A
;ANSWER
;AUTHORITY
;ADDITIONAL
# We enabled flag "CD" aka checking disabled aka please do not do any DNSSEC validation, and now doing the same query as above again:
>>> response = dns.query.udp_with_fallback(query, resolver_ip)[0]
>>> response.rcode() == dns.rcode.SERVFAIL
False
>>> response.rcode() == dns.rcode.NOERROR
True
>>> response.answer[0][0]
<DNS IN A rdata: 69.252.80.75>

fail2ban: if a line matches both failregex and ignoreregex, which one has priority?

What the subject says. I have an user who logs in using SSH through a provider that gives him a dynamic IP from a pool; all of their IPs map to static.hostingprovider.name, but that name doesn't resolve back to the same IP address.
SSH detects this and spits out the message "POSSIBLE BREAK-IN ATTEMPT", which triggers the failregex. I want to add an exception so that, if the connection attempt comes from static.hostingprovider.name, it ignores the warning. If I just add static.hostingprovider.name to the ignoreregex list, will it simply work?
In your jail.local file add this line:
ignoreip = static.hostingprovider.name

How to make rule trigger on DNS rdata/IP address?

I currently have the following DNS Query Alert rule set up in Suricata (for test purposes):
alert dns any any -> any any (msg:”Test dns_query option”; dns_query; content:”google”; nocase; sid:1;)
Which is triggered when it captures DNS events which contain the word "google", such as in this packet:
{"timestamp":"2017-06-08T15:58:59.907085+0000","flow_id":1798294020028434,"in_iface":"ens33","event_type":"dns","src_ip":"172.16.10.132","src_port":53,"dest_ip":"192.168.160.140","dest_port":52385,"proto":"UDP","dns":{"type":"answer","id":57334,"rcode":"NOERROR","rrname":"www.google.com","rrtype":"A","ttl":300,"rdata":"172.217.12.164"}}
However, instead of searching for resource record names that contain "google", I want to use this same kind of alert to trigger on IP addresses that resolve to loopback, as is the case with the following packet (Notice the rdata field):
{"timestamp":"2017-06-08T15:59:37.120927+0000","flow_id":36683121284050,"in_iface":"ens33","event_type":"dns","src_ip":"172.16.10.132","src_port":53,"dest_ip":"192.168.160.140","dest_port":62260,"proto":"UDP","dns":{"type":"answer","id":53553,"rcode":"NOERROR","rrname":"outlook1.us","rrtype":"A","ttl":120,"rdata":"127.0.0.1"}}
As I have noticed, the contentsection of a Suricata rule searches only for a string.
My current rule triggers on a text match with the rrname/domain, how would I make it so that the rule triggers on rdata/IP address?
p.s.
Just out of curiosity I tried replacing the "google" in the content section of my alert with "127.0.0.1" and that didn't work either, as expected.
The ip address is just a 32 bit number. In the rule the IP should be represented as a hex value and not a string, for purposes of efficiency and saving bandwidth (a string will be 8+ bytes as opposed to 4 bytes).
Here is my final Suricata rule to alert whenever somebody gets sent to loopback on my network:
alert dns any any -> any any (msg:"BLACKLISTED DOMAIN"; content:"|7F 00 00 01|"; sid:1;)

dig "hostname_1" #"IP" command

I was told to execute the command: dig "hostname_1" #"IP".
I don't know what it is for, any idea? and the meaning of "#IP"?
Another question, the response has the field:
;;AUTHORITY SECTION:
"hostname_1" 1200 IN NS "hostname_2"
"hostname_1" 1200 IN NS "hostname_3"
Is it correct that hostname_2 and hostname_3 are another names for hostname_1?or are they nameservers of the hostname_1 host?
dig is a tool for performing DNS lookups.
Normally dig asks your locally configured nameserver, however, with #IP you can make dig ask the nameserver which runs on the specified IP.
The output of dig can be read a follows KEY, TTL (time to live in seconds), CLASS (normally "IN" for Internet), TYPE, RDATA (resource data) (see https://en.wikipedia.org/wiki/Resource_record for a longer description)
There are a number of types (see https://en.wikipedia.org/wiki/List_of_DNS_record_types), NS means "nameserver". In your case hostname_2 and hostname_3 are the responsible nameservers for hostname_1.

Postfix and save to sent mail dir

I know this might be a dummy question or a question that comes from lack of knowledge, but I hope someone can still answer it. I did try to read a lot of Postfix documentation but found no answer to this. I don't even know if it's a Postfix specific or mail servers general question.
So I have a mail server, just a clean Postfix install that delivers email.
I've defined my users and connected with IMAP and SMTP using Thunderbird.
When I went to Thunderbird account settings and disabled "place a copy", Postfix did not put a copy of the sent message in the user .Sent folder.
However, I've also connected my Gmail, Hotmail or Yahoo mail and disabled the "place a copy" and still have a copy in the sent items folder.
So in this case there are 2 options:
Something is wrong with my Postfix configuration
Gmail, Hotmail, Yahoo put a copy in their sent folder as a different process on the server side
Just for the record, having searched around for a how to, and not finding one, I am posting it here:
The only (easy) way I've found to save sent emails is the sender_bcc solution (with it's attendant faults):
I am using postfix / dovecot / sieve / mysql virtual boxes
In /etc/postfix/main.cf add:
sender_bcc_maps = mysql:/etc/postfix/mysql-virtual-bcc-maps.cf
Create file /etc/postfix/mysql-virtual-bcc-maps.cf:
user = (database user)
password = (database password)
hosts = 127.0.0.1
dbname = (database databasename)
query = SELECT CONCAT_WS('',LEFT('%s', LOCATE('#', '%s')-1),'+sent#',SUBSTRING('%s', LOCATE('#', '%s')+1)) AS destination FROM virtual_users WHERE email='%s' AND autosent=1
You'll note in my query, I've added a (tinyint default 0) column to my virtual_users table so I can turn on/off this automatic sent items feature per user. This query takes the sender email address that postfix gives it, splits it in half at the # sign, and adds +sent to the address so it looks like sender+sent#domain.tld. This allows sieve in the next step to pick it up and drop it straight to sent items.
In /etc/dovecot/sieve/default.sieve add:
require ["fileinto", "mailbox", "envelope", "subaddress","imap4flags"];
if envelope :detail "to" "sent" {
addflag "\\Seen";
fileinto :create "Sent";
stop;
}
Also helpful to modify /etc/dovecot/conf.d/15-mailboxes.conf and add the auto subscribe to sent (and junk and trash and others for that matter):
mailbox Sent {
special_use = \Sent
auto = subscribe
}
I think that is all (I'm posting this the next day after doing it, so I think I got it all...)
Postfix itself does not place copies of sent messages anywhere; it receives messages and delivers them to the recipient. Saving sent messages to your own mailbox is the responsibility of your user agent (Thunderbird, in your case).
It's important to understand that Postfix (and other traditional Unix SMTP servers) don't have a "user" concept. Yes, if so configured it's possible to authenticate by supplying a username and a password, but Postfix doesn't use this identity information.
That said, it's not impossible to configure Postfix to do what you expected – sender_bcc_maps can be used to add a recipient to messages sent by you, and by adding yourself and using a filter in your mail client (or mail delivery agent like procmail) you can make sure that messages sent by you end up in the Sent folder.
I am running a Installation with automatic copies created by sender_bcc_maps. It's working fine. You have to check the sender, otherwise everyone can create sent mails in foreign sent folders.
I have solved it with two virtual domains. One for the user and one for the copy.
But there is a big problem with sender_bcc_maps. All bcc senders will be deleted in the sent copy. You cannot see anymore, who got a blind copy of this mail.
As 'ego2dot0' said above, you don't need any MDA filters (sieve etc.) to do this. It can be done using Postfix alone, although it took me a while to figure out how to do it.
You have to use sender_bcc_maps AND virtual_mailbox_maps features together.
You have to use a virtual domain dedicated specially for copies to self. If your actual domain is "your.domain.tld", you can use eg. subdomain "copyself.your.domain.tld". This subdomain does not have to actually exist, ie. be defined in the DNS (moreover, it's better that it isn't defined, so nobody accidentally sends mail to it from outside). It is a purely virtual domain that is recognized only by Postfix.
1) Configure sender_bcc_maps to BCC mail coming from user#your.domain.tld to user#copyself.your.domain.tld. You can do it for only a few selected users using a regular "hash" type map, or you can do it for all users at once using PCRE type map and regular expressions.
2) You have to define your virtual domain in virtual_mailbox_domains, like this:
virtual_mailbox_domains=copyself.your.domain.tld
3) Configure virtual_mailbox_maps so that the destination mailbox for address "user#copyself.your.domain.tld" is the actual "Sent" mailbox of the user "user". For example (assumed that you are using regular system users and Maildir format - like in my case) the path to "Sent" mailbox for user "user" will be "/home/user/Maildir/.Sent". So, you can define common part of the path as virtual_mailbox_base, eg.
virtual_mailbox_base=/home
and then in the virtual mailbox map enter the rest of the path like this:
user#copyself.your.domain.tld user/Maildir/.Sent/
(the trailing / is important to indicate the Maildir format).
Again, you can use PCRE type map to do this for all users.
4) To properly save mail to the mailbox, Postfix need to also know the proper UID and GID for the particular user, so you have to use virtual_uid_maps and virtual_gid_maps parameters as well. If you are using virtual users, it's probably enough to define "static" type maps specifying a single UID and GID of the system user that owns all the virtual mailboxes. However, if you are using system users like me, you need the proper actual UID and GID for any user. If you have only a few users, you can use a regular "hash" type map, with entries like these:
user#copyself.your.domain.tld 2001
or you can try to setup a pipeline with "pipemap" map type, that uses some PCRE maps and "unix:passwd.byname" map to obtain the UIDs and GIDs for all users (I haven't done this part, as my Postfix installation is compiled without "pipemap" type support).
So to sum everything up, use something like this:
In /etc/postfix/main.cf file, add the following lines:
sender_bcc_maps=hash:/etc/postfix/sender_bcc
virtual_mailbox_domains=copyself.your.domain.tld
virtual_mailbox_base=/home
virtual_mailbox_maps=hash:/etc/postfix/copyself
virtual_uid_maps=hash:/etc/postfix/copyself_uids
virtual_gid_maps=hash:/etc/postfix/copyself_gids
/etc/postfix/sender_bcc contains a bunch of lines like:
user#your.domain.tld user#copyself.your.domain.tld
/etc/postfix/copyself contains - respectively - lines like:
user#copyself.your.domain.tld user/Maildir/.Sent/
/etc/postfix/copyself_uids and /etc/postfix/copyself_gids contain - respectively - lines like:
user#copyself.your.domain.tld 2001
I have done this on my server and it works great for me.

Resources