root/trunk/pdns/pdns/pdnssec.cc @ 1757

Revision 1757, 11.3 KB (checked in by ahu, 2 years ago)

move to mature key management (unified zsks, proper ids, active, inactive)

Line 
1#include "dnsseckeeper.hh"
2#include "dnssecinfra.hh"
3#include "statbag.hh"
4#include "base32.hh"
5#include <boost/foreach.hpp>
6#include <boost/program_options.hpp>
7#include "dnsbackend.hh"
8#include "ueberbackend.hh"
9#include "arguments.hh"
10#include "packetcache.hh"
11
12StatBag S;
13PacketCache PC;
14
15using namespace boost;
16namespace po = boost::program_options;
17po::variables_map g_vm;
18
19string s_programname="pdns_server";
20
21ArgvMap &arg()
22{
23  static ArgvMap arg;
24  return arg;
25}
26
27
28string humanTime(time_t t)
29{
30  char ret[256];
31  struct tm tm;
32  localtime_r(&t, &tm);
33  strftime(ret, sizeof(ret)-1, "%c", &tm);   // %h:%M %Y-%m-%d
34  return ret;
35}
36
37void loadMainConfig()
38{
39   static char pietje[128]="!@@SYSCONFDIR@@:";
40  ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=
41    strcmp(pietje+1,"@@SYSCONFDIR@@:") ? pietje+strlen("@@SYSCONFDIR@@:")+1 : SYSCONFDIR;
42 
43  ::arg().set("launch","Which backends to launch");
44 
45  ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
46  ::arg().setCmd("help","Provide a helpful message");
47  //::arg().laxParse(argc,argv);
48
49  if(::arg().mustDo("help")) {
50    cerr<<"syntax:"<<endl<<endl;
51    cerr<<::arg().helpstring(::arg()["help"])<<endl;
52    exit(99);
53  }
54
55  if(::arg()["config-name"]!="") 
56    s_programname+="-"+::arg()["config-name"];
57
58  string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
59  cleanSlashes(configname);
60
61  cerr<<"configname: '"<<configname<<"'\n";
62 
63  ::arg().laxFile(configname.c_str());
64
65  BackendMakers().launch(::arg()["launch"]); // vrooooom!
66  ::arg().laxFile(configname.c_str());   
67  cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
68
69  S.declare("qsize-q","Number of questions waiting for database attention");
70   
71  S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
72  S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
73         
74  S.declare("query-cache-hit","Number of hits on the query cache");
75  S.declare("query-cache-miss","Number of misses on the query cache");
76  ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
77  ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no"; 
78  ::arg().set("recursive-cache-ttl","Seconds to store packets in the PacketCache")="10";
79  ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";             
80  ::arg().set("negquery-cache-ttl","Seconds to store packets in the PacketCache")="60";
81  ::arg().set("query-cache-ttl","Seconds to store packets in the PacketCache")="20";             
82  ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
83  ::arg().set("soa-retry-default","Default SOA retry")="3600";
84  ::arg().set("soa-expire-default","Default SOA expire")="604800";
85    ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
86  ::arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600";   
87  UeberBackend::go();
88}
89
90void orderZone(DNSSECKeeper& dk, const std::string& zone)
91{
92  loadMainConfig();
93  reportAllTypes(); 
94  UeberBackend* B = new UeberBackend("default");
95  SOAData sd;
96 
97  if(!B->getSOA(zone, sd)) {
98    cerr<<"No SOA!"<<endl;
99    return;
100  } 
101  cerr<<"ID: "<<sd.domain_id<<endl;
102  sd.db->list(zone, sd.domain_id);
103  DNSResourceRecord rr;
104
105  set<string> qnames;
106 
107  while(sd.db->get(rr)) {
108  //  cerr<<rr.qname<<endl;
109    qnames.insert(rr.qname);
110  }
111 
112  string salt;
113  char tmp[]={0xab, 0xcd};
114  salt.assign(tmp, 2);
115 
116  NSEC3PARAMRecordContent ns3pr;
117  dk.getNSEC3PARAM(zone, &ns3pr);
118  string hashed;
119  BOOST_FOREACH(const string& qname, qnames)
120  {
121    if(ns3pr.d_salt.empty()) // NSEC
122      sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, true);
123    else {
124      hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
125      cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
126      sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, true);
127    }
128  }
129  cerr<<"Done listing"<<endl;
130}
131
132void checkZone(DNSSECKeeper& dk, const std::string& zone)
133{
134  loadMainConfig();
135  reportAllTypes(); 
136  UeberBackend* B = new UeberBackend("default");
137  SOAData sd;
138 
139  if(!B->getSOA(zone, sd)) {
140    cerr<<"No SOA!"<<endl;
141    return;
142  } 
143  cerr<<"ID: "<<sd.domain_id<<endl;
144  sd.db->list(zone, sd.domain_id);
145  DNSResourceRecord rr;
146  uint64_t numrecords=0, numerrors=0;
147 
148    while(sd.db->get(rr)) {
149      if(rr.qtype.getCode() == QType::MX) 
150        rr.content = lexical_cast<string>(rr.priority)+" "+rr.content;
151     
152      try {
153        shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
154        string tmp=drc->serialize(rr.qname);
155      }
156      catch(std::exception& e) 
157      {
158        cerr<<"Following record had a problem: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
159        cerr<<"Error was: "<<e.what()<<endl;
160        numerrors++;
161      }
162      numrecords++;
163    }
164    cerr<<"Checked "<<numrecords<<" records, "<<numerrors<<" errors"<<endl;
165 
166 
167}
168
169
170int main(int argc, char** argv)
171try
172{
173  po::options_description desc("Allowed options");
174  desc.add_options()
175    ("help,h", "produce help message")
176    ("key-repository,k", po::value<string>()->default_value("./keys"), "Location of keys")
177    ("verbose,v", po::value<bool>(), "be verbose")
178    ("force", "force an action")
179    ("commands", po::value<vector<string> >());
180
181  po::positional_options_description p;
182  p.add("commands", -1);
183  po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
184  po::notify(g_vm);
185
186  vector<string> cmds;
187
188  if(g_vm.count("commands")) 
189    cmds = g_vm["commands"].as<vector<string> >();
190
191  if(cmds.empty() || g_vm.count("help")) {
192    cerr<<"Usage: \npdnssec [options] [show-zone] [secure-zone] [alter-zone] [order-zone] [add-zone-key] [deactivate-zone-key] [remove-zone-key] [activate-zone-key]\n";
193    cerr<<desc<<endl;
194    return 0;
195  }
196
197  DNSSECKeeper dk(g_vm["key-repository"].as<string>());
198
199  if(cmds[0] == "order-zone") {
200    if(cmds.size() != 2) {
201      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
202      return 0;
203    }
204    orderZone(dk, cmds[1]);
205  }
206  if(cmds[0] == "check-zone") {
207    if(cmds.size() != 2) {
208      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
209      return 0;
210    }
211    checkZone(dk, cmds[1]);
212  }
213#if 0
214  else if(cmds[0] == "update-zone-keys") {
215    if(cmds.size() != 2) {
216      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
217      return 0;
218    }
219
220    const string& zone=cmds[1];
221    DNSSECPrivateKey dpk;
222   
223    if(!dk.haveKSKFor(zone, &dpk)) {
224      cerr << "No KSK for zone '"<<zone<<"', can't update the ZSKs"<<endl;
225      return 0;
226    }
227    DNSSECKeeper::keyset_t zskset=dk.getZSKsFor(zone);
228
229    int inforce=0;
230    time_t now = time(&now);
231   
232   
233    if(!zskset.empty())  {
234      cout<<"There were ZSKs already for zone '"<<zone<<"': "<<endl;
235     
236      BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
237        cout<<"Tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<<endl; // ", "<<humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
238        if(value.second.active)
239          inforce++;
240        if(!value.second.active) { // was: 'expired more than two days ago' 
241          cout<<"\tThis key is no longer used and too old to keep around, deleting!\n";
242          dk.deleteZSKFor(zone, value.second.fname);
243        } else /* if( value.second.endValidity < now  ) */{ // 'expired more than two days ago' 
244          cout<<"\tThis key is no longer in active use, but needs to linger\n";
245        }
246      }
247    }
248     
249    if(inforce >= 2) {
250      cerr << "Two or more ZSKs were active already, not generating a third" << endl;
251      return 0;
252    }
253    dk.addKey(zone, true, 5);
254    dk.addKey(zone, true, 5, false); // not yet active
255
256    keyset = dk.getKeys(zone);
257    if(zskset.empty()) {
258      cerr<<"This should not happen, still no ZSK!"<<endl;
259    }
260
261    cout<<"There are now "<<zskset.size()<<" ZSKs"<<endl;
262    BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
263      cout<<"Tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<<endl;
264    }
265
266  }
267#endif
268  else if(cmds[0] == "show-zone") {
269    if(cmds.size() != 2) {
270      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
271      return 0;
272    }
273    const string& zone=cmds[1];
274   
275    DNSSECKeeper::keyset_t keyset=dk.getKeys(zone, boost::indeterminate);
276
277    if(keyset.empty())  {
278      cerr << "No keys for zone '"<<zone<<"'."<<endl;
279    }
280    else { 
281      cout << "keys: for zone '"<<zone<<"':"<<endl;
282      BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
283        cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<< endl; // humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
284        if(value.second.keyOrZone) {
285          cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
286          cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY()).getZoneRepresentation() << endl << endl;
287        }
288      }
289    }
290  }
291  else if(cmds[0] == "activate-zone-key") {
292    const string& zone=cmds[1];
293    unsigned int id=atoi(cmds[2].c_str());
294    dk.activateKey(zone, id);
295  }
296  else if(cmds[0] == "deactivate-zone-key") {
297    const string& zone=cmds[1];
298    unsigned int id=atoi(cmds[2].c_str());
299    dk.deactivateKey(zone, id);
300  }
301  else if(cmds[0] == "add-zone-key") {
302    const string& zone=cmds[1];
303    // need to get algorithm & ksk or zsk
304    dk.addKey(zone, 1, 5, 0); 
305    cerr<<"Not implemented"<<endl;
306  }
307  else if(cmds[0] == "remove-zone-key") {
308    const string& zone=cmds[1];
309    unsigned int id=atoi(cmds[2].c_str());
310    dk.removeKey(zone, id);
311  }
312 
313  else if(cmds[0] == "secure-zone") {
314    if(cmds.size() != 2) {
315      cerr << "Error: "<<cmds[0]<<" takes exactly 1 parameter"<<endl;
316      return 0;
317    }
318    const string& zone=cmds[1];
319    DNSSECPrivateKey dpk;
320   
321    if(dk.haveActiveKSKFor(zone, &dpk) && !g_vm.count("force")) {
322      cerr << "There is a key already for zone '"<<zone<<"', use --force to overwrite"<<endl;
323      return 0;
324    }
325     
326    dk.secureZone(zone, 5);
327
328    if(!dk.haveActiveKSKFor(zone, &dpk)) {
329      cerr << "This should not happen, still no key!" << endl;
330    }
331    cout<<"Created KSK with tag "<<dpk.getDNSKEY().getTag()<<endl;
332 
333    DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
334
335    if(!zskset.empty() && !g_vm.count("force"))  {
336      cerr<<"There were ZSKs already for zone '"<<zone<<"'"<<endl;
337      return 0;
338    }
339     
340    dk.addKey(zone, false, 5);
341    dk.addKey(zone, false, 5, false); // not active
342
343    zskset = dk.getKeys(zone, false);
344    if(zskset.empty()) {
345      cerr<<"This should not happen, still no ZSK!"<<endl;
346    }
347
348    cout<<"There are now "<<zskset.size()<<" ZSKs"<<endl;
349    BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
350      cout<<"id = "<<value.second.id<<", tag = "<<value.first.getDNSKEY().getTag()<<"\tActive: "<<value.second.active<<endl;
351    }
352  }
353  else {
354    cerr<<"Unknown command '"<<cmds[0]<<"'\n";
355    return 1;
356  }
357  return 0;
358}
359catch(AhuException& ae) {
360  cerr<<"Error: "<<ae.reason<<endl;
361}
Note: See TracBrowser for help on using the browser.