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 # 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. # `dnsmanagerd` needs to connect itself to authd(1) with an admin account to then authenticate its users.
# login: dnsmanager # login: dnsmanager
pass: secret pass: secret

View file

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

View file

@ -49,8 +49,8 @@ accepted_domains:
.in .in
In this example, indexes are recreated, which is related to the In this example, indexes are recreated, which is related to the
.Xr dodb 7 .Xr DoDB
document database, see the related manual page to learn more. document database, see the related documentation to learn more.
Also, there is a list of accepted domains for Also, there is a list of accepted domains for
.Xr dnsmanagerd .Xr dnsmanagerd
to handle. to handle.
@ -61,94 +61,116 @@ The following presents the complete list of configuration file variables.
Generic Generic
.Xr libipc 7 .Xr libipc 7
related variables: related variables:
.
.Bl -tag -width " print functions" -compact .Bl -tag -width " print functions" -compact
.It Li ipc_timer .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 .It Li verbosity
Int32, 4 ([0-4], Int32, 4
.br
[0-4],
.Dq 0 .Dq 0
being quiet and being quiet and
.Dq 4 .Dq 4
meaning printing debug values) 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.
.Bl -tag -width " print_ipc_message_received" -compact .It Li ipc_messages_to_show
.It Li print_ipc_timer Array of
Bool, false .Vt Baguette::Configuration::IPC::MESSAGE ,
.It Li print_ipc_connection [ERROR, EXCEPTION]
Bool, false
.It Li print_ipc_disconnection Types of IPC messages to print, for example all connections.
Bool, false This is mainly for debug since it is very low-level.
.It Li print_ipc_extra_socket High-level messages are more relevant to log.
Bool, false By default, errors and exceptions are logged.
.It Li print_ipc_message_received
Bool, false See
.It Li print_ipc_message_sent .Xr Baguette-crystal-base ,
Bool, false which includes the
.It Li print_ipc_switch .Vt Baguette::Configuration::IPC::MESSAGE
Bool, false definition.
.It Li print_ipc_error This type has an alias in
Bool, true .Xr dnsmanager :
.It Li print_ipc_exception .Vt IPCMESSAGE .
Bool, true
.It Li print_keepalive .It Li service_name
Bool, false String,
.El .Dq dnsmanager
. .br
.Xr libipc 7
unix socket name.
.El .El
Specific Specific
.Xr dnsmanagerd .Xr dnsmanagerd
variables: variables:
.Bl -tag -width "template_directory" -compact .Bl -tag -width "template_directory" -compact
.It Li service_name .It Li log_file
String, String?,
.Dq dnsmanager .Em none
(\c .br
.Xr libipc 7 Path to the log file.
unix socket name)
.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 .It Li recreate_indexes
Bool, false (see Bool, false
.Xr dodb 7 .br
man-page) See
.Xr DoDB
documentation.
.It Li storage_directory .It Li storage_directory
String, String,
.Pa ./db-dnsmanagerd .Pa ./db-dnsmanagerd
(see .br
.Xr dodb 7 See
man-page) .Xr DoDB
documentation.
.It Li login .It Li login
String,
.Dq dnsmanager
.br
.Xr dnsmanagerd .Xr dnsmanagerd
needs to connect itself to needs to connect itself to
.Xr authd 1 .Xr authd 1
with an admin account to then authenticate its users. with an admin account to then authenticate its users.
String,
.Dq dnsmanager
.It Li pass .It Li pass
String?, String?,
.Em none .Em none
.It Li template_directory .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. 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. For example, SOA and NS RRs are pre-loaded and updated by an administrator when required.
.br .br
See See
.Pa tools/write-template-zone-file.cr .Pa tools/write-template-zone-file.cr
String,
.Pa /etc/dnsmanager/templates
.It Li accepted_domains .It Li accepted_domains
List of all accepted domains. Example: netlib.re.
Array of String, [] Array of String, []
.br
List of all accepted domains. Example: netlib.re.
.El .El
.Sh Options .Sh Options
@ -191,10 +213,9 @@ Recreate database indexes (symbolic links).
Path of the directory where the Path of the directory where the
.Xr dnsmanagerd .Xr dnsmanagerd
database is stored. database is stored.
See See the
.Xr dodb 7 .Xr DoDB
for more information. documentation.
.
.It Fl -accepted-domains No domains .It Fl -accepted-domains No domains
Accepted domains, coma separated. Accepted domains, coma separated.
@ -257,24 +278,30 @@ a command-line-interface client for
. .
.It Xr mailer 1 .It Xr mailer 1
a simple executable to send mails based on templates a simple executable to send mails based on templates
.
.It Xr dodb 7
a document database library used in
.Xr authd .
.El .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 The online service
.Dq netlib.re .Dq netlib.re
provides an interface for the provides an interface for the
.Xr dnsmanagerd .Xr dnsmanagerd
daemon. daemon.
.br .br
.Lk https://netlib.re netlib.re .Lk https://www.netlib.re netlib.re
The source code of the web interface: The source code of the web interface:
.br .br
.Lk https://git.baguette.netlib.re/Baguette/dnsmanager-webclient web-client .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 .Sh Limitations
TODO: expand the documentation TODO: expand the documentation

View file

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

View file

@ -32,6 +32,7 @@ class Actions
@the_call["admin-migration-script"] = ->admin_migration_script @the_call["admin-migration-script"] = ->admin_migration_script
@the_call["admin-generate-all-zonefiles"] = ->admin_generate_all_zonefiles @the_call["admin-generate-all-zonefiles"] = ->admin_generate_all_zonefiles
@the_call["admin-provide-domain"] = ->admin_provide_domain @the_call["admin-provide-domain"] = ->admin_provide_domain
@the_call["admin-exit"] = ->admin_exit
# Domain operations. # Domain operations.
@the_call["user-domain-add"] = ->user_domain_add @the_call["user-domain-add"] = ->user_domain_add
@ -155,6 +156,12 @@ class Actions
puts "error for provide_domain: #{e.message}" puts "error for provide_domain: #{e.message}"
end end
def admin_exit
@dnsmanagerd.admin_exit
rescue e
puts "error for provide_domain: #{e.message}"
end
def user_domain_add def user_domain_add
domains = Context.args.not_nil! domains = Context.args.not_nil!
domains.each do |domain| 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" parser.banner = "COMMAND: admin migration-script user-db.txt"
unrecognized_args_to_context_args.call parser, 1, nil unrecognized_args_to_context_args.call parser, 1, nil
end 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 end
# User section. # User section.

View file

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

View file

@ -28,6 +28,11 @@ class DNSManager::Request
def initialize(@subject) def initialize(@subject)
end 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 def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user return Response::ErrorUserNotLogged.new unless user
@ -72,6 +77,11 @@ class DNSManager::Request
def initialize(@domain) def initialize(@domain)
end end
def to_s(io : IO)
super io
io << " (domain: #{@domain})"
end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON
user = dnsmanagerd.get_logged_user event user = dnsmanagerd.get_logged_user event
return Response::ErrorUserNotLogged.new unless user return Response::ErrorUserNotLogged.new unless user
@ -81,4 +91,23 @@ class DNSManager::Request
end end
end end
DNSManager.requests << GenerateZoneFile 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 end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,16 +10,15 @@ class Array(T)
end end
end end
# WIP: select (dynamically) messages to mask
module DNSManager 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 enum MESSAGE
KEEPALIVE KEEPALIVE
# TODO USETOKEN
end end
end end
alias IPCMESSAGE = Baguette::Configuration::IPC::MESSAGE
alias DNSMESSAGE = DNSManager::MESSAGE alias DNSMESSAGE = DNSManager::MESSAGE
class DNSManager::Service < IPC class DNSManager::Service < IPC
@ -51,7 +50,7 @@ class DNSManager::Service < IPC
when AuthD::Response::Login when AuthD::Response::Login
uid = response.uid uid = response.uid
token = response.token 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 else
@authd.close @authd.close
raise "Cannot authenticate to authd with login #{@configuration.login}: #{response}." raise "Cannot authenticate to authd with login #{@configuration.login}: #{response}."
@ -62,8 +61,11 @@ class DNSManager::Service < IPC
self.service_init @configuration.service_name self.service_init @configuration.service_name
end end
def get_logged_user(event : IPC::Event) def get_logged_user(fd : Int32) : AuthD::User::Public?
@logged_users[event.fd]? @logged_users[fd]?
end
def get_logged_user(event : IPC::Event) : AuthD::User::Public?
get_logged_user event.fd
end end
def decode_token(token : String) def decode_token(token : String)
@ -93,6 +95,17 @@ class DNSManager::Service < IPC
end end
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) def handle_request(event : IPC::Event)
request_start = Time.utc request_start = Time.utc
@ -106,41 +119,43 @@ class DNSManager::Service < IPC
end end
reqname = request.class.name.sub /^DNSManager::Request::/, "" reqname = request.class.name.sub /^DNSManager::Request::/, ""
connection_info_str = log_user_info event.fd
response = begin response = begin
request.handle self, event request.handle self, event
rescue e : AuthorizationException 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" Response::Error.new "authorization error"
rescue e : DomainNotFoundException 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 Response::DomainNotFound.new
rescue e : CannotCheckPermissionsException 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 Response::InsufficientRights.new
rescue e : UnknownUserException 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 Response::UnknownUser.new
rescue e : NoOwnershipException 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 Response::NoOwnership.new
rescue e : NotLoggedException 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" Response::Error.new "user not logged"
rescue e : RRNotFoundException 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 Response::RRNotFound.new
rescue e : TokenNotFoundException 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" Response::Error.new "token not found"
rescue e : RRReadOnlyException 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 Response::RRReadOnly.new e.domain, e.rr
rescue e # Generic case 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" Response::Error.new "generic error"
end end
respname = response.class.name.sub /^DNSManager::Response::/, ""
# If clients sent requests with an “id” field, it is copied # If clients sent requests with an “id” field, it is copied
# in the responses. Allows identifying responses easily. # in the responses. Allows identifying responses easily.
response.id = request.id response.id = request.id
@ -149,14 +164,14 @@ class DNSManager::Service < IPC
duration = Time.utc - request_start duration = Time.utc - request_start
response_name = response.class.name.sub /^DNSManager::Response::/, ""
if response.is_a? DNSManager::Response::Error 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 else
if reqname != "KeepAlive" || should_display? DNSMESSAGE::KEEPALIVE # Different cases where we want to simply avoid logging redundant messages.
Baguette::Log.debug "fd #{"%4d" % event.fd} (#{duration}) #{reqname} >> #{response_name}" return if reqname == "KeepAlive" && ! should_display? DNSMESSAGE::KEEPALIVE
end return if reqname == "UseToken" && respname == "Success" && ! should_display? DNSMESSAGE::USETOKEN
Baguette::Log.debug "(#{connection_info_str}) (#{duration}) #{request} >> #{response}"
end end
end end

View file

@ -7,16 +7,16 @@ require "./service.cr"
require "dodb" require "dodb"
class DNSManager::Storage 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_name : DODB::Trigger::IndexCached(Domain)
getter domains_by_share_key : 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_transfer_key : DODB::Trigger::IndexCached(Domain)
getter domains_by_owners : DODB::Trigger::Tags(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 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_uuid : DODB::Trigger::IndexCached(Token)
getter tokens_by_domain : DODB::Trigger::Partition(Token) getter tokens_by_domain : DODB::Trigger::Partition(Token)
@ -32,7 +32,7 @@ class DNSManager::Storage
end end
def initialize(@root : String, reindex : Bool = false) 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_name = @domains.new_index "name", &.name
@domains_by_share_key = @domains.new_index "share-key", do |d| @domains_by_share_key = @domains.new_index "share-key", do |d|
if k = d.share_key if k = d.share_key
@ -49,9 +49,9 @@ class DNSManager::Storage
end end
end end
@domains_by_owners = @domains.new_tags "owners", &.owners.map &.to_s @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 @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_uuid = @tokens.new_index "uuid", &.uuid
@tokens_by_domain = @tokens.new_partition "domain", &.domain @tokens_by_domain = @tokens.new_partition "domain", &.domain

View file

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