Skip to content

Commit de5285f

Browse files
Created NTP endpoints
1 parent 0bbd067 commit de5285f

8 files changed

Lines changed: 347 additions & 19 deletions

File tree

docs/documentation.json

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7404,6 +7404,105 @@
74047404
{
74057405
"name": "NTPD",
74067406
"item": [
7407+
{
7408+
"name": "TIME_SERVER",
7409+
"item": [
7410+
{
7411+
"name": "Create NTPd Time Server",
7412+
"request": {
7413+
"method": "POST",
7414+
"header": [],
7415+
"body": {
7416+
"mode": "raw",
7417+
"raw": "{\n \"timeserver\": \"ntp.pool.org\", \n \"ispool\": true, \n \"prefer\": true, \n \"noselect\": false\n}",
7418+
"options": {
7419+
"raw": {
7420+
"language": "json"
7421+
}
7422+
}
7423+
},
7424+
"url": {
7425+
"raw": "https://{{$hostname}}/api/v1/services/ntpd/time_server?timeserver=string&ispool=boolean&noselect=boolean&prefer=boolean",
7426+
"protocol": "https",
7427+
"host": [
7428+
"{{$hostname}}"
7429+
],
7430+
"path": [
7431+
"api",
7432+
"v1",
7433+
"services",
7434+
"ntpd",
7435+
"time_server"
7436+
],
7437+
"query": [
7438+
{
7439+
"key": "timeserver",
7440+
"value": "string",
7441+
"description": "Specify the IP or hostname of the NTP time server to add."
7442+
},
7443+
{
7444+
"key": "ispool",
7445+
"value": "boolean",
7446+
"description": "Specify whether or not this time server represents an NTP server pool or a single NTP server. "
7447+
},
7448+
{
7449+
"key": "noselect",
7450+
"value": "boolean",
7451+
"description": "Enable or disable this NTP time server from selection. If `true`, the time server will be eligible for selection. If `false, the NTP server will remain In the configuration but not used."
7452+
},
7453+
{
7454+
"key": "prefer",
7455+
"value": "boolean",
7456+
"description": "Enable or disable this NTP time server as a preferred NTP time server. If `true`, this time server will take preference over other time servers."
7457+
}
7458+
]
7459+
},
7460+
"description": "Add a new NTP time server to the NTPd configuration.<br><br>\n\n_Requires at least one of the following privileges:_ [`page-all`, `page-services-ntpd`]"
7461+
},
7462+
"response": []
7463+
},
7464+
{
7465+
"name": "Delete NTPd Time Server",
7466+
"request": {
7467+
"method": "DELETE",
7468+
"header": [],
7469+
"body": {
7470+
"mode": "raw",
7471+
"raw": "{\n \"timeserver\": \"ntp.pool.org\"\n}",
7472+
"options": {
7473+
"raw": {
7474+
"language": "json"
7475+
}
7476+
}
7477+
},
7478+
"url": {
7479+
"raw": "https://{{$hostname}}/api/v1/services/ntpd/time_server?timeserver=string",
7480+
"protocol": "https",
7481+
"host": [
7482+
"{{$hostname}}"
7483+
],
7484+
"path": [
7485+
"api",
7486+
"v1",
7487+
"services",
7488+
"ntpd",
7489+
"time_server"
7490+
],
7491+
"query": [
7492+
{
7493+
"key": "timeserver",
7494+
"value": "string",
7495+
"description": "Specify the IP or hostname of the NTP time server to delete. If more than one time server exists with this value, only the first match will be deleted. In the case that all time servers were deleted, the default time server `pool.ntp.org` will be written."
7496+
}
7497+
]
7498+
},
7499+
"description": "Delete an existing NTP time server from the NTPd configuration.<br><br>\n\n_Requires at least one of the following privileges:_ [`page-all`, `page-services-ntpd`]"
7500+
},
7501+
"response": []
7502+
}
7503+
],
7504+
"description": "API endpoints that create, read, update and delete NTPd configuration."
7505+
},
74077506
{
74087507
"name": "Start NTPd Service",
74097508
"request": {
@@ -7436,6 +7535,123 @@
74367535
},
74377536
"response": []
74387537
},
7538+
{
7539+
"name": "Read NTPd Service",
7540+
"protocolProfileBehavior": {
7541+
"disableBodyPruning": true
7542+
},
7543+
"request": {
7544+
"method": "GET",
7545+
"header": [],
7546+
"body": {
7547+
"mode": "raw",
7548+
"raw": "{\n \n}",
7549+
"options": {
7550+
"raw": {
7551+
"language": "json"
7552+
}
7553+
}
7554+
},
7555+
"url": {
7556+
"raw": "https://{{$hostname}}/api/v1/services/ntpd",
7557+
"protocol": "https",
7558+
"host": [
7559+
"{{$hostname}}"
7560+
],
7561+
"path": [
7562+
"api",
7563+
"v1",
7564+
"services",
7565+
"ntpd"
7566+
]
7567+
},
7568+
"description": "Read the ntpd service configuration.<br><br>\n\n_Requires at least one of the following privileges:_ [`page-all`, `page-services-ntpd`]"
7569+
},
7570+
"response": []
7571+
},
7572+
{
7573+
"name": "Update NTPd Service",
7574+
"request": {
7575+
"method": "PUT",
7576+
"header": [],
7577+
"body": {
7578+
"mode": "raw",
7579+
"raw": "{\n \"interface\": [\"WAN\", \"lan\", \"lo0\"],\n \"orphan\": 15,\n \"logpeer\": true,\n \"logsys\": true,\n \"clockstats\": true,\n \"loopstats\": false,\n \"peerstats\": true,\n \"statsgraph\": true,\n \"timeservers\": [\n {\"timeserver\": \"ntp.pool.org\", \"ispool\": true, \"prefer\": true, \"noselect\": false}\n ]\n }",
7580+
"options": {
7581+
"raw": {
7582+
"language": "json"
7583+
}
7584+
}
7585+
},
7586+
"url": {
7587+
"raw": "https://{{$hostname}}/api/v1/services/ntpd?interface=array&time_servers=array&orphan=integer&leapsec=string&statsgraph=boolean&logpeer=boolean&logsys=boolean&clockstats=boolean&loopstats=boolean&peerstats=boolean",
7588+
"protocol": "https",
7589+
"host": [
7590+
"{{$hostname}}"
7591+
],
7592+
"path": [
7593+
"api",
7594+
"v1",
7595+
"services",
7596+
"ntpd"
7597+
],
7598+
"query": [
7599+
{
7600+
"key": "interface",
7601+
"value": "array",
7602+
"description": "Update the Interfaces NTPd will listen on. This must be an array of strings. You may specify either the physical Interface ID, the pfSense Interface ID or the descriptive Interface name. To match any Interface, simply pass In an empty array. e.g. `[\"wan\", \"em1\", \"lo0\", \"LAN\"]` (optional)"
7603+
},
7604+
{
7605+
"key": "time_servers",
7606+
"value": "array",
7607+
"description": "Update the time servers used by the system. This must be an array of objects. Each object may use the parameters available In the /api/v1/services/ntpd/time_server endpoint. To revert to the default timeserver, you may pass In an empty array. Specifying this field will overwrite any existing time servers. To simply add or remove time servers, use the /api/v1/services/ntpd/time_server endpoint as it Is less taxing on the system. e.g. `[{\"timeserver\": \"pool.ntp.org\", \"ispool\": true\", \"prefer\": true, \"noselect\": false}]` (optional)"
7608+
},
7609+
{
7610+
"key": "orphan",
7611+
"value": "integer",
7612+
"description": "Update the orphan mode value. This must be a value between `1` and `15`. Orphan mode allows the system clock to be used when no other clocks are available. The number here specifies the stratum reported during orphan mode and should normally be set to a number high enough to insure that any other servers available to clients are preferred over this server. (optional)"
7613+
},
7614+
{
7615+
"key": "leapsec",
7616+
"value": "string",
7617+
"description": "Update the leap seconds configuration. This should be the contents of the leap seconds file received from the IERS. (optional)"
7618+
},
7619+
{
7620+
"key": "statsgraph",
7621+
"value": "boolean",
7622+
"description": "Enable or disable RRD graphs of NTP metrics. (optional)"
7623+
},
7624+
{
7625+
"key": "logpeer",
7626+
"value": "boolean",
7627+
"description": "Enable or disable the logging of peer messages. (optional)"
7628+
},
7629+
{
7630+
"key": "logsys",
7631+
"value": "boolean",
7632+
"description": "Enable or disable the logging of system messages. (optional)"
7633+
},
7634+
{
7635+
"key": "clockstats",
7636+
"value": "boolean",
7637+
"description": "Enable or disable the logging of reference clock statistics. (optional)"
7638+
},
7639+
{
7640+
"key": "loopstats",
7641+
"value": "boolean",
7642+
"description": "Enable or disable the logging of clock discipline statistics. (optional)"
7643+
},
7644+
{
7645+
"key": "peerstats",
7646+
"value": "boolean",
7647+
"description": "Enable or disable the logging of NTP peer statistics. (optional)"
7648+
}
7649+
]
7650+
},
7651+
"description": "Update the ntpd service configuration.<br><br>\n\n_Requires at least one of the following privileges:_ [`page-all`, `page-services-ntpd`]"
7652+
},
7653+
"response": []
7654+
},
74397655
{
74407656
"name": "Restart NTPd Service",
74417657
"request": {

pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,13 +542,13 @@ function get($id, $data=[], $all=false) {
542542
"status" => "bad request",
543543
"code" => 400,
544544
"return" => $id,
545-
"message" => "NTPd timeserver must be valid IP address or FQDN"
545+
"message" => "NTPd timeserver must be valid IP address or hostname"
546546
],
547547
2050 => [
548548
"status" => "bad request",
549549
"code" => 400,
550550
"return" => $id,
551-
"message" => "Maximum limit of 10 NTPd timeservers has been met"
551+
"message" => "Maximum limit of 10 NTPd timeservers exceeded"
552552
],
553553
2051 => [
554554
"status" => "bad request",

pfSense-pkg-API/files/etc/inc/api/models/APIServicesNTPdRead.inc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ class APIServicesNTPdRead extends APIModel {
2727
public function action() {
2828
# Return the current NTP configuration if it exists, otherwise return empty array
2929
if (isset($this->config["ntpd"])) {
30-
return APIResponse\get(0, $this->config["ntpd"]);
30+
$data = $this->config["ntpd"];
31+
$data["timeservers"] = explode(" ", $this->config["system"]["timeservers"]);
32+
return APIResponse\get(0, $data);
3133
} else {
3234
return APIResponse\get(0, []);
3335
}

pfSense-pkg-API/files/etc/inc/api/models/APIServicesNTPdTimeServerCreate.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class APIServicesNTPdTimeServerCreate extends APIModel {
3232
public function action() {
3333
# Save our updated configuration. Note this updates configuration in two different places.
3434
$this->config["ntpd"] = $this->validated_data;
35-
$this->config["system"]["timeservers"] = implode(" ", $this->timeservers);
35+
$this->config["system"]["timeservers"] = implode(" ", array_filter($this->timeservers));
3636
$this->write_config();
3737

3838
# Reconfigure NTP to apply the changes then return the response
@@ -62,7 +62,7 @@ class APIServicesNTPdTimeServerCreate extends APIModel {
6262
# NTP service settings. Do not use the validated_data property to track valid timeserver input.
6363
if (isset($this->initial_data["timeserver"])) {
6464
# Ensure the value is a valid IP address or hostname
65-
if (is_ipaddr($this->initial_data["timeserver"]) or is_fqdn($this->initial_data["timeserver"])) {
65+
if (is_ipaddr($this->initial_data["timeserver"]) or is_hostname($this->initial_data["timeserver"])) {
6666
$this->timeservers[] = $this->initial_data['timeserver'];
6767
} else {
6868
$this->errors[] = APIResponse\get(2049);

pfSense-pkg-API/files/etc/inc/api/models/APIServicesNTPdUpdate.inc

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,21 @@ class APIServicesNTPdUpdate extends APIModel {
3030
# Save our updated configuration
3131
$this->config["ntpd"] = $this->validated_data;
3232
$this->write_config();
33+
$this->__validate_timeservers(true); // Revalidate timeservers. This time create if valid.
3334

34-
# Apply our change and return the response. Include all configured timeservers from system > timeservers as it
35-
# is need to provide the full context of the configuration
35+
# Apply our change to the backend
3636
system_ntp_configure();
37+
38+
# Update our return values and return the response. Get the updated NTPd configuration and add the timeservers
39+
# from system > timeservers to provide full context of the NTP configuration.
40+
$this->validated_data = $this->config["ntpd"];
3741
$this->validated_data["timeservers"] = explode(" ", $this->config["system"]["timeservers"]);;
3842
return APIResponse\get(0, $this->validated_data);
3943
}
4044

4145
public function validate_payload() {
4246
$this->__validate_interface();
47+
$this->__validate_timeservers();
4348
$this->__validate_orphan();
4449
$this->__validate_logsys();
4550
$this->__validate_logpeer();
@@ -73,22 +78,43 @@ class APIServicesNTPdUpdate extends APIModel {
7378
}
7479
}
7580

76-
private function __validate_timeservers() {
81+
private function __validate_timeservers($create=false) {
7782
# Check for our optional 'timeservers' payload value
7883
if (isset($this->initial_data["timeservers"])) {
79-
# Loop through each requested timeserver and ensure it is valid
80-
foreach ($this->initial_data["timeservers"] as $timeserver) {
81-
# Use the APIServicesNTPdTimeServerCreate model to validate each requested time server. This essentially
82-
# creates a nested/internal API call to validate/create NTPd timeservers
83-
$cts = new APIServicesNTPdTimeServerCreate();
84-
$cts->initial_data = $timeserver;
85-
$cts->validate_payload();
86-
87-
# If errors were encountered while validating the payload, save those errors to this models error field
88-
if (!empty($cts->errors)) {
89-
$this->errors = $this->errors + $cts->errors;
84+
# Only proceed if 10 or less timeservers were specified.
85+
if (is_array($this->initial_data["timeservers"]) and count($this->initial_data["timeservers"]) <= 10) {
86+
# Assume default if empty array was passed in
87+
if (empty($this->initial_data["timeservers"])) {
88+
$this->initial_data["timeservers"][] = ["timeserver"=>"pool.ntp.org", "ispool"=>true];
89+
}
90+
91+
# Unset all existing timeservers.
92+
unset($this->config["system"]["timeservers"]);
93+
unset($this->config["ntpd"]["ispool"]);
94+
unset($this->config["ntpd"]["noselect"]);
95+
unset($this->config["ntpd"]["prefer"]);
96+
97+
# Loop through each requested timeserver and ensure it is valid
98+
foreach ($this->initial_data["timeservers"] as $timeserver) {
99+
# Use the APIServicesNTPdTimeServerCreate model to validate each requested time server. This
100+
# essentiall creates a nested/internal API call to validate/create NTPd timeservers
101+
$cts = new APIServicesNTPdTimeServerCreate();
102+
$cts->initial_data = $timeserver;
103+
$cts->validate_payload();
104+
105+
# If errors were encountered validating the payload, save those errors to this models error field
106+
if (!empty($cts->errors)) {
107+
$this->errors = $this->errors + $cts->errors;
108+
} # Otherwise, if our created parameter is true, create this timeserver after validation
109+
elseif ($create === true) {
110+
$cts->action();
111+
}
90112
}
91113
}
114+
# If our limit was exceeded return an error
115+
else {
116+
$this->errors[] = APIResponse\get(2050);
117+
}
92118
}
93119
}
94120

@@ -180,4 +206,12 @@ class APIServicesNTPdUpdate extends APIModel {
180206

181207
return $if_list;
182208
}
209+
210+
# Private method to create the validated timeservers
211+
private function __create_timeservers() {
212+
# Only proceed if timeservers were requested and no errors were found
213+
if (isset($this->initial_data["timeservers"]) and empty($this->create_timeserver->errors)) {
214+
$this->create_timeserver->action();
215+
}
216+
}
183217
}

0 commit comments

Comments
 (0)