Compare commits

...

10 commits

21 changed files with 448 additions and 104 deletions

15
apparmor.d/boilerplate Normal file
View file

@ -0,0 +1,15 @@
# This file is related to the `Baguette` project (authd, dnsmanagerd…).
# It is a way to avoid the long and complex default configuration files provided
# by the system. Allowed operations can be known in a matter of seconds.
# Accept basically all available libraries.
@{BASE_LIBS}=/{,usr/,usr/local/}lib{,32,64}/*.so* /usr/lib/x86_64*/*.so* /etc/ld*
# Enable reading files from different places required by the libraries I use,
# which may be the Crystal standard library itself.
@{BASE_RO}=/dev/{,u}random /dev/pts/* /proc/** /etc/localtime /usr/share/zoneinfo/**
@{BASE_RW}=/dev/{null,zero,full}
# Found in other profiles:
# Recent glibc uses /dev/full in preference to /dev/null for programs
# that don't have open fds at exec().

27
apparmor.d/dnsmanager Normal file
View file

@ -0,0 +1,27 @@
# Main configuration directory.
@{MAIN_CONF_DIR}=@{HOME}/.config/baguette
# Main configuration files.
@{AUTHD_CONFIG}=@{MAIN_CONF_DIR}/auth.yml
@{DNSMANAGERD_CONFIG}=@{MAIN_CONF_DIR}/dnsmanager.yml
# Databases.
@{AUTHD_DB_PATH}=@{HOME}/tmp/db-authd
@{DNSMANAGERD_DB_PATH}=@{HOME}/tmp/db-dnsmanagerd
# Key to encrypt passwords.
@{AUTHD_DB_KEY}=@{MAIN_CONF_DIR}/authd-db-key
# DNS templates (read-only entries).
@{DNSMANAGERD_TEMPLATES}=@{MAIN_CONF_DIR}/templates/*.json
# Logs.
@{LOGS_DIR}=@{HOME}/tmp/logs
@{AUTHD_LOGS}=@{LOGS_DIR}/auth
@{DNSMANAGERD_LOGS}=@{LOGS_DIR}/dnsmanager
# Mailer for authd.
@{MAILER}=/{usr,usr/local}/bin/mailer
# IPC-related directory (see libipc(7)).
@{LIBIPC_DIR}=/tmp/.libipc-run/

View file

@ -0,0 +1,30 @@
abi <abi/3.0>,
include <tunables/global>
include <dnsmanager>
include <boilerplate>
/usr/local/bin/dnsmanagerd flags=(enforce) {
# See the file `boilerplate`.
@{BASE_LIBS} mr,
@{BASE_RO} r,
@{BASE_RW} rw,
# Allow IPC-related unix sockets.
owner @{LIBIPC_DIR}/* rwk,
# Enable all unix socket operations. TODO: restrict this even further?
unix,
# Deny networking (udp and tcp).
deny network tcp,
deny network udp,
# Configuration and DNS templates.
owner @{DNSMANAGERD_CONFIG} r,
owner @{DNSMANAGERD_TEMPLATES} r,
# Database and logs.
owner @{DNSMANAGERD_DB_PATH}/** rwkl,
owner @{DNSMANAGERD_LOGS} w,
}

View file

@ -1,5 +1,8 @@
# Configuration to put in ~/.config/baguette/dnsmanager.yml or in /etc/baguette/dnsmanager.yml
# Path to the log file. By default, everything is just printed on screen.
log_file: /var/log/baguette/dnsmanager.log
# `dnsmanagerd` needs to connect itself to authd(1) with an admin account to then authenticate its users.
# login: dnsmanager
pass: secret

View file

@ -39,7 +39,7 @@ COMMAND:
.Bl -tag -width " print functions"
.It Li maintenance
Maintenance operation of the website
.br
Example:
.Ar admin maintenance Subject
.Op value
@ -52,7 +52,7 @@ for Verbosity is the verbosity-level [0-4].
.It Li genzone
Generate a zone file
.br
Usage:
.Ar admin genzone domain
.br
@ -61,13 +61,19 @@ Example:
.It Li genall
Generate all zone files
.br
Usage:
.Ar admin genall
.It Li exit
Kill the service.
.br
Usage:
.Ar admin exit
.It Li provide-domain
Create a domain for someone
.br
Usage:
.Ar admin provide-domain login domain
.br
@ -76,7 +82,7 @@ Example:
.It Li migration-script
Migrate domains from dnsmanager v1; this shouldn't be a concern for anyone.
.br
Usage:
.Ar admin migration-script user-db.txt
.br
@ -97,7 +103,7 @@ COMMAND:
.Bl -tag -width " print functions"
.It Li domain
Domain operations
.br
Available commands:
.br
.Em user domain add domain No [domain...]
@ -108,12 +114,12 @@ Available commands:
.It Li zonefile
Get a the generated zonefile
.br
Usage:
.Em user zonefile domain No [domain...]
.It Li zone
Zone operations
.br
Usage:
.br
.Em user zone add No <file> [<file>...]
@ -125,7 +131,7 @@ Usage:
.Em user zone list
.It Li rr
Zone Resource Record operations
.br
Usage:
.br
.Em user rr add No [A|AAAA|CNAME|MX|SRV|TXT|NS]
@ -236,12 +242,12 @@ a command-line-interface client for
a DNS manager service using
.Xr authd
to handle users (authentication, authorization, preferences and profile)
.
.It
.Xr dodb 7
a document database library used in
.Xr authd .
.El
The Document-oriented DataBase (DoDB) library used in
.Xr dnsmanagerd .
.br
.Lk https://git.baguette.netlib.re/Baguette/dodb.cr dodb
.Sh Limitations
TODO: expand the documentation

View file

@ -49,8 +49,8 @@ accepted_domains:
.in
In this example, indexes are recreated, which is related to the
.Xr dodb 7
document database, see the related manual page to learn more.
.Xr DoDB
document database, see the related documentation to learn more.
Also, there is a list of accepted domains for
.Xr dnsmanagerd
to handle.
@ -61,94 +61,116 @@ The following presents the complete list of configuration file variables.
Generic
.Xr libipc 7
related variables:
.
.Bl -tag -width " print functions" -compact
.It Li ipc_timer
Int32, 30_000 (30 seconds)
.
Int32, 30_000
.br
The IPC timer wakes the process by default every 30 seconds.
There is no much point changing this value since nothing is executed periodically anyway, at least for now.
.It Li verbosity
Int32, 4 ([0-4],
Int32, 4
.br
[0-4],
.Dq 0
being quiet and
.Dq 4
meaning printing debug values)
.
.It Li print functions
Print functions enable to select messages to print, for example by printing a message each time a message is received while ignoring keepalive messages.
meaning printing debug values.
.Bl -tag -width " print_ipc_message_received" -compact
.It Li print_ipc_timer
Bool, false
.It Li print_ipc_connection
Bool, false
.It Li print_ipc_disconnection
Bool, false
.It Li print_ipc_extra_socket
Bool, false
.It Li print_ipc_message_received
Bool, false
.It Li print_ipc_message_sent
Bool, false
.It Li print_ipc_switch
Bool, false
.It Li print_ipc_error
Bool, true
.It Li print_ipc_exception
Bool, true
.It Li print_keepalive
Bool, false
.El
.
.It Li ipc_messages_to_show
Array of
.Vt Baguette::Configuration::IPC::MESSAGE ,
[ERROR, EXCEPTION]
Types of IPC messages to print, for example all connections.
This is mainly for debug since it is very low-level.
High-level messages are more relevant to log.
By default, errors and exceptions are logged.
See
.Xr Baguette-crystal-base ,
which includes the
.Vt Baguette::Configuration::IPC::MESSAGE
definition.
This type has an alias in
.Xr dnsmanager :
.Vt IPCMESSAGE .
.It Li service_name
String,
.Dq dnsmanager
.br
.Xr libipc 7
unix socket name.
.El
Specific
.Xr dnsmanagerd
variables:
.Bl -tag -width "template_directory" -compact
.It Li service_name
String,
.Dq dnsmanager
(\c
.Xr libipc 7
unix socket name)
.It Li log_file
String?,
.Em none
.br
Path to the log file.
.It Li messages_to_mask
Array of
.Vt DNSManager::MESSAGE ,
.Em [ KEEPALIVE ]
.br
List of high-level
.Em dnsmanagerd
messages to mask in the logs.
.br
The type
.Vt DNSManager::MESSAGE
has an alias:
.Vt DNSMESSAGE .
.It Li recreate_indexes
Bool, false (see
.Xr dodb 7
man-page)
Bool, false
.br
See
.Xr DoDB
documentation.
.It Li storage_directory
String,
.Pa ./db-dnsmanagerd
(see
.Xr dodb 7
man-page)
.br
See
.Xr DoDB
documentation.
.It Li login
String,
.Dq dnsmanager
.br
.Xr dnsmanagerd
needs to connect itself to
.Xr authd 1
with an admin account to then authenticate its users.
String,
.Dq dnsmanager
.It Li pass
String?,
.Em none
.It Li template_directory
String,
.Pa /etc/dnsmanager/templates
.br
New domains require to load a template so users won't have to enter some necessary entries themselves.
For example, SOA and NS RRs are pre-loaded and updated by an administrator when required.
.br
See
.Pa tools/write-template-zone-file.cr
String,
.Pa /etc/dnsmanager/templates
.It Li accepted_domains
List of all accepted domains. Example: netlib.re.
Array of String, []
.br
List of all accepted domains. Example: netlib.re.
.El
.Sh Options
@ -191,10 +213,9 @@ Recreate database indexes (symbolic links).
Path of the directory where the
.Xr dnsmanagerd
database is stored.
See
.Xr dodb 7
for more information.
.
See the
.Xr DoDB
documentation.
.It Fl -accepted-domains No domains
Accepted domains, coma separated.
@ -257,24 +278,30 @@ a command-line-interface client for
.
.It Xr mailer 1
a simple executable to send mails based on templates
.
.It Xr dodb 7
a document database library used in
.Xr authd .
.El
The Document-oriented DataBase (DoDB) library used in
.Xr dnsmanagerd .
.br
.Lk https://git.baguette.netlib.re/Baguette/dodb.cr dodb
The online service
.Dq netlib.re
provides an interface for the
.Xr dnsmanagerd
daemon.
.br
.Lk https://netlib.re netlib.re
.Lk https://www.netlib.re netlib.re
The source code of the web interface:
.br
.Lk https://git.baguette.netlib.re/Baguette/dnsmanager-webclient web-client
The logging and configuration library for the whole
.Dq baguette
project.
.br
.Lk https://git.baguette.netlib.re/Baguette/baguette-crystal-base baguette-crystal-base
.Sh Limitations
TODO: expand the documentation

View file

@ -181,6 +181,11 @@ class DNSManager::Client < IPC
parse_message [ Response::Success ], read
end
def admin_exit
request = Request::Exit.new
send_now request
end
#
# Utils
#

View file

@ -32,6 +32,7 @@ class Actions
@the_call["admin-migration-script"] = ->admin_migration_script
@the_call["admin-generate-all-zonefiles"] = ->admin_generate_all_zonefiles
@the_call["admin-provide-domain"] = ->admin_provide_domain
@the_call["admin-exit"] = ->admin_exit
# Domain operations.
@the_call["user-domain-add"] = ->user_domain_add
@ -155,6 +156,12 @@ class Actions
puts "error for provide_domain: #{e.message}"
end
def admin_exit
@dnsmanagerd.admin_exit
rescue e
puts "error for provide_domain: #{e.message}"
end
def user_domain_add
domains = Context.args.not_nil!
domains.each do |domain|

View file

@ -131,6 +131,14 @@ def parsing_cli(authd_config : Baguette::Configuration::Auth)
parser.banner = "COMMAND: admin migration-script user-db.txt"
unrecognized_args_to_context_args.call parser, 1, nil
end
# Kill the service.
parser.on("exit", "Kill the service.") do
Baguette::Log.info "kill the service."
Context.command = "admin-exit"
parser.banner = "COMMAND: exit"
unrecognized_args_to_context_args.call parser, nil, 0
end
end
# User section.

View file

@ -5,6 +5,10 @@ class IPC::JSON
def handle(service : IPC, event : IPC::Event)
raise "unimplemented"
end
def to_s(io : IO)
io << self.class.name.sub /[^:]+::[^:]+::/, ""
end
end
module DNSManager

View file

@ -28,6 +28,11 @@ class DNSManager::Request
def initialize(@subject)
end
def to_s(io : IO)
super io
io << " (subject: #{@subject}, int: #{@int}, string: #{@string})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -72,6 +77,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -81,4 +91,23 @@ class DNSManager::Request
end
end
DNSManager.requests << GenerateZoneFile
IPC::JSON.message Exit, 248 do
def initialize
end
def to_s(io : IO)
super io
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
dnsmanagerd.storage.user_must_be_admin! user.uid
Baguette::Log.warning "exit requested, bye"
exit 0
end
end
DNSManager.requests << Exit
end

View file

@ -10,6 +10,11 @@ class DNSManager::Request
def initialize(@login, @domain)
end
def to_s(io : IO)
super io
io << " (login: #{@login}, domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user

View file

@ -5,6 +5,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -20,6 +25,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -35,6 +45,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -50,6 +65,11 @@ class DNSManager::Request
def initialize(@uuid)
end
def to_s(io : IO)
super io
io << " (uuid: #{@uuid})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user

View file

@ -9,6 +9,11 @@ class DNSManager::Request
def initialize(@domain, @rrid)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain}, rrid: #{@rrid})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -24,6 +29,11 @@ class DNSManager::Request
def initialize(@token, @address)
end
def to_s(io : IO)
super io
io << " (token: #{@token[0..15]}..., address: #{@address})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
dnsmanagerd.storage.use_token @token, @address
end

View file

@ -44,6 +44,11 @@ class DNSManager::Request
def initialize(@user_id = nil)
end
def to_s(io : IO)
super io
io << " (user_id: #{@user_id})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user

View file

@ -9,6 +9,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -24,6 +29,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -38,6 +48,11 @@ class DNSManager::Request
def initialize(@zone)
end
def to_s(io : IO)
super io
io << " (zone: #{@zone})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -52,6 +67,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -77,6 +97,11 @@ class DNSManager::Request
property domain : String
property rr : DNSManager::Storage::Zone::ResourceRecord
def to_s(io : IO)
super io
io << " (domain: #{@domain}, rr: #{@rr.to_simple_s})"
end
def initialize(@domain, @rr)
end
@ -95,6 +120,11 @@ class DNSManager::Request
def initialize(@domain, @rr)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain}, rr: #{@rr.to_simple_s})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -110,6 +140,11 @@ class DNSManager::Request
def initialize(@domain, @rrid)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain}, rrid: #{@rrid})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user
@ -124,6 +159,11 @@ class DNSManager::Request
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user

View file

@ -4,6 +4,11 @@ class DNSManager::Response
property reason : String | Array(String)
def initialize(@reason)
end
def to_s(io : IO)
super io
io << " (reason: #{@reason})"
end
end
DNSManager.responses << Error

View file

@ -3,6 +3,11 @@ class DNSManager::Response
property domain : String
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
end
DNSManager.responses << DomainDeleted
@ -11,6 +16,11 @@ class DNSManager::Response
property errors : Array(Storage::Zone::Error)
def initialize(@errors)
end
def to_s(io : IO)
super io
io << " (errors: #{@errors.join ","})"
end
end
DNSManager.responses << InvalidZone
@ -18,6 +28,11 @@ class DNSManager::Response
property domain : DNSManager::Storage::Domain
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain.name})"
end
end
DNSManager.responses << DomainChanged
@ -25,6 +40,11 @@ class DNSManager::Response
property zone : Storage::Zone
def initialize(@zone)
end
def to_s(io : IO)
super io
io << " (zone: #{@zone})"
end
end
DNSManager.responses << Zone
@ -38,6 +58,11 @@ class DNSManager::Response
property domains : Array(String)
def initialize(@domains)
end
def to_s(io : IO)
super io
io << " (domains: #{@domains.join ","})"
end
end
DNSManager.responses << DomainList
@ -45,6 +70,11 @@ class DNSManager::Response
property domains : Array(String)
def initialize(@domains)
end
def to_s(io : IO)
super io
io << " (domains: #{@domains.join ","})"
end
end
DNSManager.responses << AcceptedDomains
@ -54,6 +84,11 @@ class DNSManager::Response
property my_domains : Array(DNSManager::Storage::Domain)
def initialize(@admin, @accepted_domains, @my_domains)
end
def to_s(io : IO)
super io
io << " (admin: #{@admin})"
end
end
DNSManager.responses << Logged
@ -61,6 +96,11 @@ class DNSManager::Response
property domain : String
def initialize(@domain)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
end
DNSManager.responses << DomainAdded
@ -68,6 +108,11 @@ class DNSManager::Response
property rrid : UInt32
def initialize(@rrid)
end
def to_s(io : IO)
super io
io << " (rrid: #{@rrid})"
end
end
DNSManager.responses << RRDeleted
@ -76,6 +121,11 @@ class DNSManager::Response
property rr : Storage::Zone::ResourceRecord
def initialize(@domain, @rr)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain}, rr: #{@rr.to_simple_s})"
end
end
DNSManager.responses << RRAdded
@ -84,6 +134,11 @@ class DNSManager::Response
property errors : Array(Storage::Zone::Error)
def initialize(@errors)
end
def to_s(io : IO)
super io
io << " (errors: #{@errors.join ","})"
end
end
DNSManager.responses << InvalidRR
@ -92,6 +147,11 @@ class DNSManager::Response
property rr : Storage::Zone::ResourceRecord
def initialize(@domain, @rr)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain}, rr: #{@rr.to_simple_s})"
end
end
DNSManager.responses << RRUpdated
@ -100,6 +160,11 @@ class DNSManager::Response
property rr : Storage::Zone::ResourceRecord
def initialize(@domain, @rr)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain}, rr: #{@rr.to_simple_s})"
end
end
DNSManager.responses << RRReadOnly
@ -108,6 +173,11 @@ class DNSManager::Response
property zonefile : String
def initialize(@domain, @zonefile)
end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
end
DNSManager.responses << GeneratedZone
@ -115,6 +185,11 @@ class DNSManager::Response
property domains : Array(String)
def initialize(@domains)
end
def to_s(io : IO)
super io
io << " (domains: #{@domains.join ","})"
end
end
DNSManager.responses << OrphanDomainList
end

View file

@ -10,16 +10,15 @@ class Array(T)
end
end
# WIP: select (dynamically) messages to mask
module DNSManager
# Select messages to mask in the logs (when everything goes well, of course).
# No need to flood the logs with keepalive messages or periodic token use, for instance.
enum MESSAGE
KEEPALIVE
# TODO
USETOKEN
end
end
alias IPCMESSAGE = Baguette::Configuration::IPC::MESSAGE
alias DNSMESSAGE = DNSManager::MESSAGE
class DNSManager::Service < IPC
@ -51,7 +50,7 @@ class DNSManager::Service < IPC
when AuthD::Response::Login
uid = response.uid
token = response.token
Baguette::Log.info "Authenticated as #{@configuration.login} #{uid}, token: #{token}"
Baguette::Log.info "Authenticated as #{@configuration.login} #{uid}, token: #{token[0..15]}..."
else
@authd.close
raise "Cannot authenticate to authd with login #{@configuration.login}: #{response}."
@ -62,8 +61,11 @@ class DNSManager::Service < IPC
self.service_init @configuration.service_name
end
def get_logged_user(event : IPC::Event)
@logged_users[event.fd]?
def get_logged_user(fd : Int32) : AuthD::User::Public?
@logged_users[fd]?
end
def get_logged_user(event : IPC::Event) : AuthD::User::Public?
get_logged_user event.fd
end
def decode_token(token : String)
@ -93,6 +95,17 @@ class DNSManager::Service < IPC
end
end
# `log_user_info` provides a string composed from either the user
# id in case the user was authenticated or the file descriptor of
# the connection.
def log_user_info(fd : Int32) : String
if user = get_logged_user fd
"userid #{user.uid}"
else
"fd #{"%4d" % fd}"
end
end
def handle_request(event : IPC::Event)
request_start = Time.utc
@ -106,41 +119,43 @@ class DNSManager::Service < IPC
end
reqname = request.class.name.sub /^DNSManager::Request::/, ""
connection_info_str = log_user_info event.fd
response = begin
request.handle self, event
rescue e : AuthorizationException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} authorization error"
Baguette::Log.error "(#{connection_info_str}) #{request} authorization error"
Response::Error.new "authorization error"
rescue e : DomainNotFoundException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} domain not found"
Baguette::Log.error "(#{connection_info_str}) #{request} domain not found"
Response::DomainNotFound.new
rescue e : CannotCheckPermissionsException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} cannot check permissions of user '#{e.uid}' on resource '#{e.resource}'"
Baguette::Log.error "(#{connection_info_str}) #{request} cannot check permissions of user '#{e.uid}' on resource '#{e.resource}'"
Response::InsufficientRights.new
rescue e : UnknownUserException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} unknown user"
Baguette::Log.error "(#{connection_info_str}) #{request} unknown user"
Response::UnknownUser.new
rescue e : NoOwnershipException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} no ownership error"
Baguette::Log.error "(#{connection_info_str}) #{request} no ownership error"
Response::NoOwnership.new
rescue e : NotLoggedException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} user not logged"
Baguette::Log.error "(#{connection_info_str}) #{request} user not logged"
Response::Error.new "user not logged"
rescue e : RRNotFoundException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} RR not found"
Baguette::Log.error "(#{connection_info_str}) #{request} RR not found"
Response::RRNotFound.new
rescue e : TokenNotFoundException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} Token not found"
Baguette::Log.error "(#{connection_info_str}) #{request} Token not found"
Response::Error.new "token not found"
rescue e : RRReadOnlyException
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} RR is read only"
Baguette::Log.error "(#{connection_info_str}) #{request} RR is read only"
Response::RRReadOnly.new e.domain, e.rr
rescue e # Generic case
Baguette::Log.error "(fd #{"%4d" % event.fd}) #{reqname} generic error #{e}"
Baguette::Log.error "(#{connection_info_str}) #{request} generic error #{e}"
Response::Error.new "generic error"
end
respname = response.class.name.sub /^DNSManager::Response::/, ""
# If clients sent requests with an “id” field, it is copied
# in the responses. Allows identifying responses easily.
response.id = request.id
@ -149,14 +164,14 @@ class DNSManager::Service < IPC
duration = Time.utc - request_start
response_name = response.class.name.sub /^DNSManager::Response::/, ""
if response.is_a? DNSManager::Response::Error
Baguette::Log.warning "fd #{"%4d" % event.fd} (#{duration}) #{reqname} >> #{response_name} (#{response.reason})"
Baguette::Log.warning "(#{connection_info_str}) (#{duration}) #{request} >> #{response}"
else
if reqname != "KeepAlive" || should_display? DNSMESSAGE::KEEPALIVE
Baguette::Log.debug "fd #{"%4d" % event.fd} (#{duration}) #{reqname} >> #{response_name}"
end
# Different cases where we want to simply avoid logging redundant messages.
return if reqname == "KeepAlive" && ! should_display? DNSMESSAGE::KEEPALIVE
return if reqname == "UseToken" && respname == "Success" && ! should_display? DNSMESSAGE::USETOKEN
Baguette::Log.debug "(#{connection_info_str}) (#{duration}) #{request} >> #{response}"
end
end

View file

@ -7,16 +7,16 @@ require "./service.cr"
require "dodb"
class DNSManager::Storage
getter domains : DODB::Storage::Cached(Domain)
getter domains : DODB::Storage::Common(Domain)
getter domains_by_name : DODB::Trigger::IndexCached(Domain)
getter domains_by_share_key : DODB::Trigger::IndexCached(Domain)
getter domains_by_transfer_key : DODB::Trigger::IndexCached(Domain)
getter domains_by_owners : DODB::Trigger::Tags(Domain)
getter zones : DODB::Storage::Cached(Zone)
getter zones : DODB::Storage::Common(Zone)
getter zones_by_domain : DODB::Trigger::IndexCached(Zone)
getter tokens : DODB::Storage::Cached(Token)
getter tokens : DODB::Storage::Common(Token)
getter tokens_by_uuid : DODB::Trigger::IndexCached(Token)
getter tokens_by_domain : DODB::Trigger::Partition(Token)
@ -32,7 +32,7 @@ class DNSManager::Storage
end
def initialize(@root : String, reindex : Bool = false)
@domains = DODB::Storage::Cached(Domain).new "#{@root}/domains"
@domains = DODB::Storage::Common(Domain).new "#{@root}/domains", 5_000
@domains_by_name = @domains.new_index "name", &.name
@domains_by_share_key = @domains.new_index "share-key", do |d|
if k = d.share_key
@ -49,9 +49,9 @@ class DNSManager::Storage
end
end
@domains_by_owners = @domains.new_tags "owners", &.owners.map &.to_s
@zones = DODB::Storage::Cached(Zone).new "#{@root}/zones"
@zones = DODB::Storage::Common(Zone).new "#{@root}/zones", 5_000
@zones_by_domain = @zones.new_index "domain", &.domain
@tokens = DODB::Storage::Cached(Token).new "#{@root}/tokens"
@tokens = DODB::Storage::Common(Token).new "#{@root}/tokens", 5_000
@tokens_by_uuid = @tokens.new_index "uuid", &.uuid
@tokens_by_domain = @tokens.new_partition "domain", &.domain

View file

@ -88,6 +88,10 @@ class DNSManager::Storage::Zone
[] of Error
end
def to_simple_s
"(rrid #{@rrid}, '#{@name}' #{@rrtype})"
end
def to_s(io : IO)
io << "(#{ "%4d" % @rrid }) "
io << "#{ "%.30s" % @name} #{ "%6d" % @ttl} #{ "%.10s" % @rrtype } #{ "%.30s" % @target}\n"
@ -913,6 +917,10 @@ class DNSManager::Storage::Zone
end
def to_s(io : IO)
io << "ZONE #{@domain} (#{@resources.size} rr)"
end
def pretty_print(io : IO)
io << "DOMAIN #{@domain}.\n"
@resources.each do |rr|
io << rr