.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "GDNSDCTL 8" .TH GDNSDCTL 8 "2024-01-05" "gdnsd 3.8.2" "gdnsd" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" gdnsdctl \- Control socket client for gdnsd .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 10 \& Usage: gdnsdctl [\-c /etc/gdnsd] [\-s ] [\-D] [\-l] [\-t 47] [\-o] [\-i] [...] \& \-c \- Configuration directory (def /etc/gdnsd), for finding UNIX control socket path \& \-s \- TCP control socket address \& \-D \- Enable verbose debug output \& \-l \- Send logs to syslog rather than stderr \& \-t \- Timeout in seconds (def 47, range 5 \- 300) \& \-o \- One\-shot mode: do not retry soft failures (comms errors, replace\-in\-progress) \& \-i \- Ignore lack of a running daemon for stop, reload\-zones, replace, \& and acme\-dns\-01\-flush, reporting success instead of failure in those cases. \& Actions: \& stop \- Stops the running daemon \& reload\-zones \- Reload the running daemon\*(Aqs zone data \& replace \- Ask daemon to spawn a takeover replacement of itself (updates code, config, zone data) \& status \- Checks the running daemon\*(Aqs status \& stats \- Dumps JSON statistics from the running daemon \& states \- Dumps JSON monitored states \& acme\-dns\-01 \- Create ACME DNS\-01 payloads from additional arguments: \& ... [max %u payloads] \& acme\-dns\-01\-flush \- Flush (remove) all ACME DNS\-01 payloads added above .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBgdnsdctl\fR is the canonical control socket client for \fBgdnsd\fR. All operations described above are synchronous and report success by exiting with exit status zero, or failure by non-zero. .PP In general gdnsd and gdnsdctl support concurrent execution of disparate actions from multiple actors against a single daemon, and everything should eventually faithfully report success without any bugs caused by races, if there are no other issues. .PP That is to say, for example, things should be reliable if your configuration management is routinely executing \f(CW\*(C`gdnsdctl replace\*(C'\fR for configuration updates, and another tool is independently routinely executing \f(CW\*(C`gdnsdctl reload\-zones\*(C'\fR as it updates zonefile data, while a third set of tooling is doing \f(CW\*(C`gdnsdctl acme\-dns\-01\*(C'\fR commands, even if all 3 commands collide with near-perfect timing. Eventually all 3 \f(CW\*(C`gdnsdctl\*(C'\fR commands will exit, and the resulting state will have all of the configuration, zone data, and challenge updates. .PP This is also true of multiple independent and overlapping executions of the same action, such as multiple \f(CW\*(C`reload\-zones\*(C'\fR requests proceeding in parallel, or multiple \f(CW\*(C`acme\-dns\-01\*(C'\fR requests. .SH "COMMANDLINE OPTION FLAGS" .IX Header "COMMANDLINE OPTION FLAGS" .IP "\fB\-c\fR" 4 .IX Item "-c" Set the configuration directory, defaults to \fI/etc/gdnsd\fR. This is used to find the main daemon's configuration file and parse it, in case it sets a non-default \f(CW\*(C`run_dir\*(C'\fR, which would change the location of the daemon's \&\s-1UNIX\s0 control socket path. .IP "\fB\-s\fR" 4 .IX Item "-s" This tells gdnsdctl to connect to the daemon over a \s-1TCP\s0 control socket instead of the usual local \s-1UNIX\s0 domain socket. See the \fBgdnsd.config\fR\|(5) docs about \&\f(CW\*(C`tcp_control\*(C'\fR for more information about configuring the server side of this. .IP "\fB\-D\fR" 4 .IX Item "-D" Enables additional debug-level log output as appropriate. .IP "\fB\-l\fR" 4 .IX Item "-l" Sends log output to syslog rather than the default stderr. .IP "\fB\-t\fR" 4 .IX Item "-t" Sets the timeout in seconds (default 47) for the entire execution. This is enforced via a process-level \s-1SIGALRM\s0 timer, but it can run slightly over time in corner cases. .IP "\fB\-o\fR" 4 .IX Item "-o" One-shot mode. Normally, gdnsdctl will persistently retry to execute the action under soft failure conditions. The soft failure conditions happen when the daemon is in the midst of a \f(CW\*(C`replace\*(C'\fR operation, or when a communications error occurs after the connection is initially established (which could be the racy result of catching bad timing with the old daemon shutting down during a \&\f(CW\*(C`replace\*(C'\fR operation). This disables the retries and upgrades all soft (retryable) errors to hard (immediately-fatal) errors. .IP "\fB\-i\fR" 4 .IX Item "-i" Ignore-dead-daemon mode. Normally, all actions fail if no daemon is currently running. With this flag set, if the daemon is not currently running, but the action is one of the limited set where the intent would be accurately reflected by a future start of the daemon, gdnsdctl will return a successful exit value in spite of the lack of a running daemon. .Sp The set of commands which currently honor \f(CW\*(C`\-i\*(C'\fR are: \f(CW\*(C`reload\-zones\*(C'\fR, \f(CW\*(C`stop\*(C'\fR, \&\f(CW\*(C`acme\-dns\-01\-flush\*(C'\fR, and \f(CW\*(C`replace\*(C'\fR. .SH "Actions" .IX Header "Actions" .IP "\fBstop\fR" 4 .IX Item "stop" Synchronously stops the running daemon. Exit status zero means the daemon was observed to stop as commanded. .Sp With \f(CW\*(C`\-i\*(C'\fR, exit status zero can also mean the daemon was already not running, which makes \f(CW\*(C`gdnsdctl \-i stop\*(C'\fR an idempotent stop. .IP "\fBreload-zones\fR" 4 .IX Item "reload-zones" Synchronously reloads the daemon's zonefiles. Exit status zero indicates the operation completed successfully and the new data is visible to clients. .Sp With \f(CW\*(C`\-i\*(C'\fR, exit status zero can also mean the daemon was not running (if it's started in the future, the recently-updated zonefiles would be in effect). .IP "\fBreplace\fR" 4 .IX Item "replace" Asks the running daemon to spawn a replacement instance of itself. This can be used for daemon code upgrades and/or configuration changes. The \f(CW\*(C`replace\*(C'\fR sequence is intended to have minimal operational impact from most points of view: it's race-free, requests are not lost or dropped and there is no pause in processing requests. Stats data is handed off seamlessly from the old instance to the new one, as is any ephemeral \s-1ACME DNS\-01\s0 challenge data. The control socket remains available for new connections at all times, but the actual processing of new connections is delayed for a short span of time. .Sp Commands from already-connected control socket clients (like gdnsdctl) can be denied with a soft failure response during critical time windows of the replace sequence, but gdnsdctl will wait and retry them afterwards. .Sp The replacement daemon is initially executed as a child of the running daemon and thus inherits many attributes of its execution environment. The old and new daemons coordinate a handoff process over an inter-daemon control socket connection. The handoff process does its best to ensure that if the new daemon fails at any stage of startup, the old daemon can continue operations as it was before. .Sp \&\s-1DNS\s0 listening sockets are handed off in an overlapped fashion: there will be a brief window of time where client requests can be routed to either daemon randomly, but there should never be a window where service is unavailable. The control socket itself is handed off synchronously: the old daemon stops accepting new connections before the new daemon begins accepting them, and the old daemon re-starts accepting if the new daemon fails to succeed completely. .Sp Once the old daemon claims the replacement process was successful, gdnsdctl will monitor the old daemon's exit and then make a fresh connection to the control socket of the new daemon and check its status. If all of this is successful, the replace command will finally exit with status zero. It will exit with a non-zero status if any part of this operation fails for any reason, which should in most conceivable cases leave the existing daemon fully functional. .Sp All other daemon-state-changing gdnsdctl operations (anything but the readonly actions \*(L"status\*(R", \*(L"stats\*(R", and \*(L"states\*(R") which might be initiated separately and concurrently are explicitly blocked by the daemon while the critical part of the replace handoff sequence is in progress. gdnsdctl will, by default, retry an action repeatedly until the replace finishes and lets the action through or it reaches the \f(CW\*(C`\-t\*(C'\fR timeout. The \f(CW\*(C`\-o\*(C'\fR flag disables these retries, which will cause the state-changing gdnsdctl actions to immediately fail during the critical replace window. .Sp Normally, this command will fail if there is no running daemon. However, with \&\f(CW\*(C`\-i\*(C'\fR, exit status zero can also mean the daemon was not running (if it's started in the future, the recently-updated configuration or binary that triggered wanting to execute the \f(CW\*(C`replace\*(C'\fR request would then be in effect). .Sp The retry system works for \f(CW\*(C`replace\*(C'\fR vs \f(CW\*(C`replace\*(C'\fR as well: if you concurrently execute several \f(CW\*(C`gdnsdctl replace\*(C'\fR commands, one will proceed first and the rest will wait in retry loops to each take their turn replacing the daemon serially until they've all done so successfully, unless their gdnsdctl \f(CW\*(C`\-t\*(C'\fR timeout expires first (each new daemon will take some time to start, depending on the zonefile count and configuration complexity). .IP "\fBstatus\fR" 4 .IX Item "status" Checks the running daemon's status, reporting its \s-1PID\s0 and version to stderr. .IP "\fBstats\fR" 4 .IX Item "stats" Dumps \s-1JSON\s0 statistics from the running daemon to stdout. .IP "\fBstates\fR" 4 .IX Item "states" Dumps \s-1JSON\s0 monitored states from any configured service health monitors. .IP "\fBacme\-dns\-01\fR" 4 .IX Item "acme-dns-01" Injects temporary \s-1ACME DNS\-01\s0 challenge response payloads as defined by into the running daemon. This is intended for integration with scripts or services which generate signed certificates from an ACME-capable certificate authority. .Sp Two or more additional commandline arguments are required, in pairs of \f(CW\*(C`name\*(C'\fR and \f(CW\*(C`payload\*(C'\fR, where \f(CW\*(C`name\*(C'\fR is a valid domainname and \f(CW\*(C`payload\*(C'\fR is the challenge response payload (which, as a \s-1SHA\-256\s0 output encoded in base64url encoding, should be 43 bytes long and consist of only alphanumeric characters plus \f(CW\*(C`\-\*(C'\fR and \f(CW\*(C`_\*(C'\fR). A maximum of 100 separate payloads can be specified in a single gdnsdctl invocation. .Sp This example... .Sp .Vb 1 \& gdnsdctl acme\-dns\-01 example.org 0123456789012345678901234567890123456789012 www.example.org ABCDEFGHIJKLMNOPQRSTUVWXYZ\-abcdefghijklmnop .Ve .Sp \&... causes the daemon to temporarily respond to \f(CW\*(C`TXT\*(C'\fR requests for the name \&\f(CW\*(C`_acme\-challenge.example.org.\*(C'\fR with the first payload above, and similarly with the second payload for \f(CW\*(C`_acme\-challenge.www.example.org.\*(C'\fR. .Sp Challenge payload responses injected by this command automatically expire after a short time. The default is 10 minutes, and is configurable via the config option \f(CW\*(C`acme_challenge_ttl\*(C'\fR. The actual \s-1DNS TTL\s0 emitted with the \f(CW\*(C`TXT\*(C'\fR responses defaults to zero (which is highly recommended!) and is controlled by the separate option \f(CW\*(C`acme_challenge_dns_ttl\*(C'\fR. .Sp Injecting responses for domainnames that are not within the scope of one of the statically-configured zones will succeed, but the response to such queries will still be \f(CW\*(C`REFUSED\*(C'\fR (until such a zone later appears, if ever). .Sp If more than one payload is configured for a given name (in the same command, or in separate commands less than the \s-1TTL\s0 window apart), multiple \s-1TXT\s0 records will be emitted. If there is any statically-configured \s-1TXT\s0 data from zonefiles at a conflicting \f(CW\*(C`_acme\-challenge\*(C'\fR name, the static \s-1TXT\s0 RRs will also be served alongside any data from this mechanism. .Sp As an implementail detail (quirk?) of this mechanism, any static zonefile RRs which have a first label of \f(CW\*(C`_acme\-challenge\*(C'\fR will automatically have their TTLs forced to the \f(CW\*(C`acme_challenge_dns_ttl\*(C'\fR regardless of the \s-1TTL\s0 specified in the zonefile. This was the easiest way to ensure that we never serve mixed \s-1TTL\s0 values in a single RR-set of \s-1TXT\s0 records, which is forbidden by \s-1RFC 2181.\s0 .Sp The data injected by this mechanism persists through daemon \f(CW\*(C`replace\*(C'\fR operations, but not through a full \f(CW\*(C`stop\*(C'\fR of the daemon. It also persists through zone data reloads, and in the case that data is injected for a non-existent zone which then comes into existence through a reload, the challenge will begin working after the reload. .IP "\fBacme\-dns\-01\-flush\fR" 4 .IX Item "acme-dns-01-flush" Immediately flushes all injected payload data from above ahead of its natural expiry. Mostly useful for testing or for clearing up mistakes, but some integrations with spiky large volumes of challenges may wish to flush data explicitly at points in time when there are no outstanding \s-1DNS\-01\s0 challenges. .Sp This is another command that honors the \f(CW\*(C`\-i\*(C'\fR flag: if \f(CW\*(C`\-i\*(C'\fR is specified and the daemon is not running, this command will report success, as a dead daemon has no challenge data to flush. .SH "EXIT STATUS" .IX Header "EXIT STATUS" In general, all operations exit with status zero if and only if the operation is successful. Errors and most other output go to stderr, except in the case of \s-1JSON\s0 data dumps, which go to stdout. .SH "SEE ALSO" .IX Header "SEE ALSO" \&\fBgdnsd\fR\|(8), \fBgdnsd.config\fR\|(5), \fBgdnsd.zonefile\fR\|(5) .PP The gdnsd manual. .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" Copyright (c) 2012 Brandon L Black .PP This file is part of gdnsd. .PP gdnsd is free software: you can redistribute it and/or modify it under the terms of the \s-1GNU\s0 General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. .PP gdnsd is distributed in the hope that it will be useful, but \s-1WITHOUT ANY WARRANTY\s0; without even the implied warranty of \&\s-1MERCHANTABILITY\s0 or \s-1FITNESS FOR A PARTICULAR PURPOSE.\s0 See the \&\s-1GNU\s0 General Public License for more details. .PP You should have received a copy of the \s-1GNU\s0 General Public License along with gdnsd. If not, see .