From 782e9b24c390a954ab88fec1b1d019f200f9d448 Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Wed, 21 Jul 2021 16:26:55 +0300 Subject: [PATCH] auth: add support for autoprimary management via API and pdnsutil --- docs/http-api/autoprimaries.rst | 71 +++++++++++++++ docs/http-api/index.rst | 1 + .../swagger/authoritative-api-swagger.yaml | 89 +++++++++++++++++++ docs/manpages/pdnsutil.1.rst | 4 + modules/bindbackend/bindbackend2.cc | 25 ++++++ modules/bindbackend/bindbackend2.hh | 3 +- modules/gmysqlbackend/gmysqlbackend.cc | 2 + modules/godbcbackend/godbcbackend.cc | 2 + modules/gpgsqlbackend/gpgsqlbackend.cc | 2 + modules/gsqlite3backend/gsqlite3backend.cc | 2 + pdns/backends/gsql/gsqlbackend.cc | 55 ++++++++++-- pdns/backends/gsql/gsqlbackend.hh | 12 ++- pdns/dnsbackend.hh | 25 +++++- pdns/pdnsutil.cc | 45 +++++++++- pdns/ueberbackend.cc | 20 ++++- pdns/ueberbackend.hh | 4 +- pdns/ws-api.cc | 5 +- pdns/ws-auth.cc | 52 +++++++++++ 18 files changed, 404 insertions(+), 15 deletions(-) create mode 100644 docs/http-api/autoprimaries.rst diff --git a/docs/http-api/autoprimaries.rst b/docs/http-api/autoprimaries.rst new file mode 100644 index 0000000000..ab269d4d7c --- /dev/null +++ b/docs/http-api/autoprimaries.rst @@ -0,0 +1,71 @@ +Autoprimaries +============= + +This API is used to manage :ref:`autoprimaries `. + +Autoprimary endpoints +--------------------- + +.. openapi:: swagger/authoritative-api-swagger.yaml + :paths: /servers/{server_id}/autoprimaries /servers/{server_id}/autoprimaries/{ip}/{nameserver} + +Objects +------- + +An autoprimary object represents a single autoprimary server. + +.. openapi:: swagger/authoritative-api-swagger.yaml + :definitions: Autoprimary + +Examples +-------- + +Listing autoprimaries +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: http + + GET /servers/localhost/autoprimaries HTTP/1.1 + X-Api-Key: secret + Content-Type: application/json + +Will yield a response similar to this (several headers omitted): + +.. code-block:: http + + HTTP/1.1 200 Ok + Content-Type: application/json + + [{"ip":"192.0.2.1","nameserver":"ns.example.com","account":""},{"ip":"192.0.2.50","nameserver":"ns.example.org","account":"example"}] + +Creating an autoprimary +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: http + + POST /servers/localhost/autoprimaries HTTP/1.1 + X-Api-Key: secret + Content-Type: application/json + + {"ip":"192.0.2.1","nameserver":"ns.example.com","account":""} + +Will yield a response similar to this (several headers omitted): + +.. code-block:: http + + HTTP/1.1 201 Created + +Deleting an autoprimary +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: http + + DELETE /servers/localhost/autoprimaries/192.0.2.1/ns.example.com HTTP/1.1 + X-Api-Key: secret + Content-Type: application/json + +Will yield a response similar to this (several headers omitted): + +.. code-block:: http + + HTTP/1.1 204 No Content diff --git a/docs/http-api/index.rst b/docs/http-api/index.rst index 60b449fff9..256033f9f4 100644 --- a/docs/http-api/index.rst +++ b/docs/http-api/index.rst @@ -367,6 +367,7 @@ The API exposes several endpoints and objects: cryptokey metadata tsigkey + autoprimaries search statistics cache diff --git a/docs/http-api/swagger/authoritative-api-swagger.yaml b/docs/http-api/swagger/authoritative-api-swagger.yaml index cc05c1e49e..3761061858 100644 --- a/docs/http-api/swagger/authoritative-api-swagger.yaml +++ b/docs/http-api/swagger/authoritative-api-swagger.yaml @@ -864,6 +864,81 @@ paths: description: 'OK, key was deleted' <<: *commonErrors + '/servers/{server_id}/autoprimaries': + parameters: + - name: server_id + in: path + required: true + description: 'The id of the server to retrieve the key from' + type: string + get: + summary: 'Get a list of autoprimaries' + operationId: getAutoprimaries + tags: + - autoprimary + responses: + '200': + description: OK. + schema: + $ref: '#/definitions/Autoprimary' + <<: *commonErrors + post: + summary: 'Add an autoprimary' + description: 'This methods add a new autoprimary server.' + operationId: createAutoprimary + tags: + - autoprimary + parameters: + - name: ip + description: IP address for autoprimary server + required: true + in: body + schema: + $ref: '#/definitions/Autoprimary' + - name: nameserver + description: DNS name for autoprimary server + required: true + in: body + schema: + $ref: '#/definitions/Autoprimary' + - name: account + description: Optional account name + required: false + in: body + schema: + $ref: '#/definitions/Autoprimary' + responses: + '201': + description: Created + <<: *commonErrors + + '/servers/{server_id}/autoprimaries/{ip}/{nameserver}': + parameters: + - name: server_id + in: path + required: true + description: 'The id of the server to retrieve to delete the autoprimary from' + type: string + - name: ip + in: path + required: true + description: 'IP address of autoprimary' + type: string + - name: nameserver + in: path + required: true + description: 'DNS name of the autoprimary' + type: string + delete: + summary: 'Delete the autoprimary entry' + operationId: deleteAutoprimary + tags: + - autoprimary + responses: + '204': + description: 'OK, key was deleted' + <<: *commonErrors + definitions: Server: title: Server @@ -1071,6 +1146,20 @@ definitions: description: 'Set to "TSIGKey"' readOnly: true + Autoprimary: + title: Autoprimary server + description: An autoprimary server that can provision new domains. + properties: + ip: + type: string + description: "IP address of the autoprimary server" + nameserver: + type: string + description: "DNS name of the autoprimary server" + account: + type: string + description: "Account name for the autoprimary server" + ConfigSetting: title: ConfigSetting properties: diff --git a/docs/manpages/pdnsutil.1.rst b/docs/manpages/pdnsutil.1.rst index ca086d6112..f614095567 100644 --- a/docs/manpages/pdnsutil.1.rst +++ b/docs/manpages/pdnsutil.1.rst @@ -164,6 +164,10 @@ add-record *ZONE* *NAME* *TYPE* [*TTL*] *CONTENT* and optional *TTL*. If *TTL* is not set, default will be used. add-autoprimary *IP* *NAMESERVER* [*ACCOUNT*] Add a autoprimary entry into the backend. This enables receiving zone updates from other servers. +remove-autoprimary *IP* *NAMESERVER* + Remove an autoprimary from backend. Not supported by BIND backend. +list-autoprimaries + List all autoprimaries. create-zone *ZONE* Create an empty zone named *ZONE*. create-secondary-zone *ZONE* *PRIMARY* [*PRIMARY*].. diff --git a/modules/bindbackend/bindbackend2.cc b/modules/bindbackend/bindbackend2.cc index 2987ee5b77..43e2034999 100644 --- a/modules/bindbackend/bindbackend2.cc +++ b/modules/bindbackend/bindbackend2.cc @@ -1333,6 +1333,31 @@ bool Bind2Backend::handle::get_list(DNSResourceRecord& r) return false; } +bool Bind2Backend::autoPrimariesList(std::vector& primaries) +{ + if (getArg("supermaster-config").empty()) + return false; + + ifstream c_if(getArg("supermasters"), std::ios::in); + if (!c_if) { + g_log << Logger::Error << "Unable to open supermasters file for read: " << stringerror() << endl; + return false; + } + + string line, sip, saccount; + while (getline(c_if, line)) { + std::istringstream ii(line); + ii >> sip; + if (sip.size() != 0) { + ii >> saccount; + primaries.emplace_back(sip, "", saccount); + } + } + + c_if.close(); + return true; +} + bool Bind2Backend::superMasterBackend(const string& ip, const DNSName& domain, const vector& nsset, string* nameserver, string* account, DNSBackend** db) { // Check whether we have a configfile available. diff --git a/modules/bindbackend/bindbackend2.hh b/modules/bindbackend/bindbackend2.hh index db65ff266f..ef5e2f0c25 100644 --- a/modules/bindbackend/bindbackend2.hh +++ b/modules/bindbackend/bindbackend2.hh @@ -236,7 +236,8 @@ public: void parseZoneFile(BB2DomainInfo* bbd); void rediscover(string* status = nullptr) override; - // for supermaster support + // for autoprimary support + bool autoPrimariesList(std::vector& primaries) override; bool superMasterBackend(const string& ip, const DNSName& domain, const vector& nsset, string* nameserver, string* account, DNSBackend** db) override; static std::mutex s_supermaster_config_lock; bool createSlaveDomain(const string& ip, const DNSName& domain, const string& nameserver, const string& account) override; diff --git a/modules/gmysqlbackend/gmysqlbackend.cc b/modules/gmysqlbackend/gmysqlbackend.cc index 97c010ebdd..fcea02024c 100644 --- a/modules/gmysqlbackend/gmysqlbackend.cc +++ b/modules/gmysqlbackend/gmysqlbackend.cc @@ -107,6 +107,8 @@ public: declare(suffix, "supermaster-query", "", "select account from supermasters where ip=? and nameserver=?"); declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=? and account=?"); declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values (?,?,?)"); + declare(suffix, "autoprimary-remove", "", "delete from supermasters where ip = ? and nameserver = ?"); + declare(suffix, "list-autoprimaries", "", "select ip,nameserver,account from supermasters"); declare(suffix, "insert-zone-query", "", "insert into domains (type,name,master,account,last_check,notified_serial) values(?,?,?,?,NULL,NULL)"); diff --git a/modules/godbcbackend/godbcbackend.cc b/modules/godbcbackend/godbcbackend.cc index ee28a581b6..87e7d4d828 100644 --- a/modules/godbcbackend/godbcbackend.cc +++ b/modules/godbcbackend/godbcbackend.cc @@ -87,6 +87,8 @@ public: declare(suffix, "supermaster-query", "", "select account from supermasters where ip=? and nameserver=?"); declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=? and account=?"); declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values (?,?,?)"); + declare(suffix, "autoprimary-remove", "", "delete from supermasters where ip = ? and nameserver = ?"); + declare(suffix, "list-autoprimaries", "", "select ip,nameserver,account from supermasters"); declare(suffix, "insert-zone-query", "", "insert into domains (type,name,master,account,last_check,notified_serial) values(?,?,?,?,null,null)"); diff --git a/modules/gpgsqlbackend/gpgsqlbackend.cc b/modules/gpgsqlbackend/gpgsqlbackend.cc index 6156374022..8bf8501d5e 100644 --- a/modules/gpgsqlbackend/gpgsqlbackend.cc +++ b/modules/gpgsqlbackend/gpgsqlbackend.cc @@ -114,6 +114,8 @@ public: declare(suffix, "supermaster-query", "", "select account from supermasters where ip=$1 and nameserver=$2"); declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=$1 and account=$2"); declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values ($1,$2,$3)"); + declare(suffix, "autoprimary-remove", "", "delete from supermasters where ip = $1 and nameserver = $2"); + declare(suffix, "list-autoprimaries", "", "select ip,nameserver,account from supermasters"); declare(suffix, "insert-zone-query", "", "insert into domains (type,name,master,account,last_check, notified_serial) values($1,$2,$3,$4,null,null)"); diff --git a/modules/gsqlite3backend/gsqlite3backend.cc b/modules/gsqlite3backend/gsqlite3backend.cc index f0df57213e..fd50ddcb60 100644 --- a/modules/gsqlite3backend/gsqlite3backend.cc +++ b/modules/gsqlite3backend/gsqlite3backend.cc @@ -100,6 +100,8 @@ public: declare(suffix, "supermaster-query", "", "select account from supermasters where ip=:ip and nameserver=:nameserver"); declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=:nameserver and account=:account"); declare(suffix, "supermaster-add", "", "insert into supermasters (ip, nameserver, account) values (:ip,:nameserver,:account)"); + declare(suffix, "autoprimary-remove", "", "delete from supermasters where ip = :ip and nameserver = :nameserver"); + declare(suffix, "list-autoprimaries", "", "select ip,nameserver,account from supermasters"); declare(suffix, "insert-zone-query", "", "insert into domains (type,name,master,account,last_check,notified_serial) values(:type, :domain, :masters, :account, null, null)"); diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 4bb6e9c617..39530fb224 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -71,6 +71,8 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix) d_SuperMasterInfoQuery=getArg("supermaster-query"); d_GetSuperMasterIPs=getArg("supermaster-name-to-ips"); d_AddSuperMaster=getArg("supermaster-add"); + d_RemoveAutoPrimaryQuery=getArg("autoprimary-remove"); + d_ListAutoPrimariesQuery=getArg("list-autoprimaries"); d_InsertZoneQuery=getArg("insert-zone-query"); d_InsertRecordQuery=getArg("insert-record-query"); d_UpdateMasterOfZoneQuery=getArg("update-master-query"); @@ -141,6 +143,8 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix) d_SuperMasterInfoQuery_stmt = nullptr; d_GetSuperMasterIPs_stmt = nullptr; d_AddSuperMaster_stmt = nullptr; + d_RemoveAutoPrimary_stmt = nullptr; + d_ListAutoPrimaries_stmt = nullptr; d_InsertZoneQuery_stmt = nullptr; d_InsertRecordQuery_stmt = nullptr; d_InsertEmptyNonTerminalOrderQuery_stmt = nullptr; @@ -1242,26 +1246,67 @@ skiprow: return false; } -bool GSQLBackend::superMasterAdd(const string &ip, const string &nameserver, const string &account) +bool GSQLBackend::superMasterAdd(const AutoPrimary& primary) { try{ reconnectIfNeeded(); d_AddSuperMaster_stmt -> - bind("ip",ip)-> - bind("nameserver",nameserver)-> - bind("account",account)-> + bind("ip",primary.ip)-> + bind("nameserver",primary.nameserver)-> + bind("account",primary.account)-> execute()-> reset(); } catch (SSqlException &e){ - throw PDNSException("GSQLBackend unable to insert a supermaster with IP " + ip + " and nameserver name '" + nameserver + "' and account '" + account + "': " + e.txtReason()); + throw PDNSException("GSQLBackend unable to insert an autoprimary with IP " + primary.ip + " and nameserver name '" + primary.nameserver + "' and account '" + primary.account + "': " + e.txtReason()); } return true; } +bool GSQLBackend::autoPrimaryRemove(const AutoPrimary& primary) +{ + try{ + reconnectIfNeeded(); + + d_RemoveAutoPrimary_stmt -> + bind("ip",primary.ip)-> + bind("nameserver",primary.nameserver)-> + execute()-> + reset(); + + } + catch (SSqlException &e){ + throw PDNSException("GSQLBackend unable to remove an autoprimary with IP " + primary.ip + " and nameserver name '" + primary.nameserver + "': " + e.txtReason()); + } + return true; + +} + +bool GSQLBackend::autoPrimariesList(std::vector& primaries) +{ + try{ + reconnectIfNeeded(); + + d_ListAutoPrimaries_stmt-> + execute()-> + getResult(d_result)-> + reset(); + } + catch (SSqlException &e){ + throw PDNSException("GSQLBackend unable to list autoprimaries: " + e.txtReason()); + } + + for(const auto& row : d_result) { + ASSERT_ROW_COLUMNS("list-autoprimaries", row, 3); + primaries.emplace_back(row[0], row[1], row[2]); + } + + return true; +} + bool GSQLBackend::superMasterBackend(const string &ip, const DNSName &domain, const vector&nsset, string *nameserver, string *account, DNSBackend **ddb) { // check if we know the ip/ns couple in the database diff --git a/pdns/backends/gsql/gsqlbackend.hh b/pdns/backends/gsql/gsqlbackend.hh index 66fc767579..365fd69a8a 100644 --- a/pdns/backends/gsql/gsqlbackend.hh +++ b/pdns/backends/gsql/gsqlbackend.hh @@ -67,6 +67,8 @@ protected: d_SuperMasterInfoQuery_stmt = d_db->prepare(d_SuperMasterInfoQuery, 2); d_GetSuperMasterIPs_stmt = d_db->prepare(d_GetSuperMasterIPs, 2); d_AddSuperMaster_stmt = d_db->prepare(d_AddSuperMaster, 3); + d_RemoveAutoPrimary_stmt = d_db->prepare(d_RemoveAutoPrimaryQuery, 2); + d_ListAutoPrimaries_stmt = d_db->prepare(d_ListAutoPrimariesQuery, 0); d_InsertZoneQuery_stmt = d_db->prepare(d_InsertZoneQuery, 4); d_InsertRecordQuery_stmt = d_db->prepare(d_InsertRecordQuery, 9); d_InsertEmptyNonTerminalOrderQuery_stmt = d_db->prepare(d_InsertEmptyNonTerminalOrderQuery, 4); @@ -131,6 +133,8 @@ protected: d_SuperMasterInfoQuery_stmt.reset(); d_GetSuperMasterIPs_stmt.reset(); d_AddSuperMaster_stmt.reset(); + d_RemoveAutoPrimary_stmt.reset(); + d_ListAutoPrimaries_stmt.reset(); d_InsertZoneQuery_stmt.reset(); d_InsertRecordQuery_stmt.reset(); d_InsertEmptyNonTerminalOrderQuery_stmt.reset(); @@ -196,7 +200,9 @@ public: bool createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector& masters, const string& account) override; bool createSlaveDomain(const string& ip, const DNSName& domain, const string& nameserver, const string& account) override; bool deleteDomain(const DNSName &domain) override; - bool superMasterAdd(const string &ip, const string &nameserver, const string &account) override; + bool superMasterAdd(const AutoPrimary& primary) override; + bool autoPrimaryRemove(const AutoPrimary& primary) override; + bool autoPrimariesList(std::vector& primaries) override; bool superMasterBackend(const string &ip, const DNSName &domain, const vector&nsset, string *nameserver, string *account, DNSBackend **db) override; void setStale(uint32_t domain_id) override; void setFresh(uint32_t domain_id) override; @@ -289,6 +295,8 @@ private: string d_GetSuperMasterName; string d_GetSuperMasterIPs; string d_AddSuperMaster; + string d_RemoveAutoPrimaryQuery; + string d_ListAutoPrimariesQuery; string d_InsertZoneQuery; string d_InsertRecordQuery; @@ -361,6 +369,8 @@ private: unique_ptr d_SuperMasterInfoQuery_stmt; unique_ptr d_GetSuperMasterIPs_stmt; unique_ptr d_AddSuperMaster_stmt; + unique_ptr d_RemoveAutoPrimary_stmt; + unique_ptr d_ListAutoPrimaries_stmt; unique_ptr d_InsertZoneQuery_stmt; unique_ptr d_InsertRecordQuery_stmt; unique_ptr d_InsertEmptyNonTerminalOrderQuery_stmt; diff --git a/pdns/dnsbackend.hh b/pdns/dnsbackend.hh index c63033d748..84689c8087 100644 --- a/pdns/dnsbackend.hh +++ b/pdns/dnsbackend.hh @@ -105,6 +105,17 @@ struct TSIGKey { std::string key; }; +struct AutoPrimary { + AutoPrimary(const string& new_ip, const string& new_nameserver, const string& new_account) { + this->ip = new_ip; + this->nameserver = new_nameserver; + this->account = new_account; + }; + std::string ip; + std::string nameserver; + std::string account; +}; + class DNSPacket; //! This virtual base class defines the interface for backends for PowerDNS. @@ -348,11 +359,23 @@ public: void setArgPrefix(const string &prefix); //! Add an entry for a super master - virtual bool superMasterAdd(const string &ip, const string &nameserver, const string &account) + virtual bool superMasterAdd(const struct AutoPrimary& primary) { return false; } + //! Remove an entry for a super master + virtual bool autoPrimaryRemove(const struct AutoPrimary& primary) + { + return false; + } + + //! List all SuperMasters, returns false if feature not supported. + virtual bool autoPrimariesList(std::vector& primaries) + { + return false; + } + //! determine if ip is a supermaster or a domain virtual bool superMasterBackend(const string &ip, const DNSName &domain, const vector&nsset, string *nameserver, string *account, DNSBackend **db) { diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index 562780989f..0605bd387c 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -1606,18 +1606,45 @@ static int addOrReplaceRecord(bool addOrReplace, const vector& cmds) { return EXIT_SUCCESS; } -// addSuperMaster add anew super primary +// addSuperMaster add a new autoprimary static int addSuperMaster(const std::string &IP, const std::string &nameserver, const std::string &account) { UeberBackend B("default"); - - if ( B.superMasterAdd(IP, nameserver, account) ){ + const AutoPrimary primary(IP, nameserver, account); + if ( B.superMasterAdd(primary) ){ return EXIT_SUCCESS; } cerr<<"could not find a backend with autosecondary support"< primaries; + if ( !B.autoPrimariesList(primaries) ){ + cerr<<"could not find a backend with autosecondary support"< 3 ? cmds.at(3) : "")); } + else if (cmds.at(0) == "remove-autoprimary") { + if(cmds.size() < 3) { + cerr << "Syntax: pdnsutil remove-autoprimary IP NAMESERVER" << endl; + return 0; + } + exit(removeAutoPrimary(cmds.at(1), cmds.at(2))); + } + else if (cmds.at(0) == "list-autoprimaries") { + exit(listAutoPrimaries()); + } else if (cmds.at(0) == "replace-rrset") { if(cmds.size() < 5) { cerr<superMasterAdd(ip, nameserver, account)) + if(backend->superMasterAdd(primary)) return true; return false; } +bool UeberBackend::autoPrimaryRemove(const AutoPrimary &primary) +{ + for(auto backend : backends) + if(backend->autoPrimaryRemove(primary)) + return true; + return false; +} + +bool UeberBackend::autoPrimariesList(std::vector& primaries) +{ + for(auto backend : backends) + if(backend->autoPrimariesList(primaries)) + return true; + return false; +} + bool UeberBackend::superMasterBackend(const string &ip, const DNSName &domain, const vector&nsset, string *nameserver, string *account, DNSBackend **db) { for(auto backend : backends) diff --git a/pdns/ueberbackend.hh b/pdns/ueberbackend.hh index 53e1e8de63..3ee7364427 100644 --- a/pdns/ueberbackend.hh +++ b/pdns/ueberbackend.hh @@ -49,7 +49,9 @@ public: bool superMasterBackend(const string &ip, const DNSName &domain, const vector&nsset, string *nameserver, string *account, DNSBackend **db); - bool superMasterAdd(const string &ip, const string &nameserver, const string &account); + bool superMasterAdd(const AutoPrimary &primary); + bool autoPrimaryRemove(const struct AutoPrimary& primary); + bool autoPrimariesList(std::vector& primaries); /** Tracks all created UeberBackend instances for us. We use this vector to notify existing threads of new modules diff --git a/pdns/ws-api.cc b/pdns/ws-api.cc index 470035386e..5c48fdae1a 100644 --- a/pdns/ws-api.cc +++ b/pdns/ws-api.cc @@ -97,7 +97,10 @@ static Json getServerDetail() { { "daemon_type", productTypeApiType() }, { "version", getPDNSVersion() }, { "config_url", "/api/v1/servers/localhost/config{/config_setting}" }, - { "zones_url", "/api/v1/servers/localhost/zones{/zone}" } + { "zones_url", "/api/v1/servers/localhost/zones{/zone}" }, +#ifndef RECURSOR + { "autoprimaries_url", "/api/v1/servers/localhost/autoprimaries{/autoprimary}" } +#endif }; } diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 03ed5c3f13..9ed87e5645 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -1603,6 +1603,56 @@ static void apiServerTSIGKeyDetail(HttpRequest* req, HttpResponse* resp) { } } +static void apiServerAutoprimaryDetail(HttpRequest* req, HttpResponse* resp) { + UeberBackend B; + if (req->method == "DELETE") { + const AutoPrimary primary(req->parameters["ip"], req->parameters["nameserver"], ""); + if (!B.autoPrimaryRemove(primary)) + throw HttpInternalServerErrorException("Cannot find backend with autoprimary feature"); + resp->body = ""; + resp->status = 204; + } else { + throw HttpMethodNotAllowedException(); + } +} + +static void apiServerAutoprimaries(HttpRequest* req, HttpResponse* resp) { + UeberBackend B; + + if (req->method == "GET") { + std::vector primaries; + if (!B.autoPrimariesList(primaries)) + throw HttpInternalServerErrorException("Unable to retrieve autoprimaries"); + Json::array doc; + for (const auto& primary: primaries) { + Json::object obj = { + { "ip", primary.ip }, + { "nameserver", primary.nameserver }, + { "account", primary.account } + }; + doc.push_back(obj); + } + resp->setJsonBody(doc); + } else if (req->method == "POST") { + auto document = req->json(); + AutoPrimary primary(stringFromJson(document, "ip"), stringFromJson(document, "nameserver"), ""); + + if (document["account"].is_string()) { + primary.account = document["account"].string_value(); + } + + if (primary.ip=="" or primary.nameserver=="") { + throw ApiException("ip and nameserver fields must be filled"); + } + if (!B.superMasterAdd(primary)) + throw HttpInternalServerErrorException("Cannot find backend with autoprimary feature"); + resp->body = ""; + resp->status = 201; + } else { + throw HttpMethodNotAllowedException(); + } +} + static void apiServerZones(HttpRequest* req, HttpResponse* resp) { UeberBackend B; DNSSECKeeper dk(&B); @@ -2366,6 +2416,8 @@ void AuthWebServer::webThread() d_ws->registerApiHandler("/api/v1/servers/localhost/config", apiServerConfig); d_ws->registerApiHandler("/api/v1/servers/localhost/search-data", apiServerSearchData); d_ws->registerApiHandler("/api/v1/servers/localhost/statistics", apiServerStatistics); + d_ws->registerApiHandler("/api/v1/servers/localhost/autoprimaries//", &apiServerAutoprimaryDetail); + d_ws->registerApiHandler("/api/v1/servers/localhost/autoprimaries", &apiServerAutoprimaries); d_ws->registerApiHandler("/api/v1/servers/localhost/tsigkeys/", apiServerTSIGKeyDetail); d_ws->registerApiHandler("/api/v1/servers/localhost/tsigkeys", apiServerTSIGKeys); d_ws->registerApiHandler("/api/v1/servers/localhost/zones//axfr-retrieve", apiServerZoneAxfrRetrieve); -- 2.47.2