{"id":377,"date":"2023-07-20T13:48:43","date_gmt":"2023-07-20T17:48:43","guid":{"rendered":"https:\/\/mooney.gatech.edu\/security\/?page_id=377"},"modified":"2024-10-22T18:16:31","modified_gmt":"2024-10-22T22:16:31","slug":"server","status":"publish","type":"page","link":"https:\/\/mooney.gatech.edu\/security\/gridtrust\/server\/","title":{"rendered":"GridTrust Server Setup"},"content":{"rendered":"\r\n<h1><span style=\"font-size: 24pt;\">GridTrust Server Setup<\/span><\/h1>\r\n<p>&nbsp;<\/p>\r\n<p><a href=\"http:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-374\" src=\"http:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2.png\" alt=\"\" width=\"4131\" height=\"1848\" srcset=\"https:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2.png 4131w, https:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2-300x134.png 300w, https:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2-1024x458.png 1024w, https:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2-768x344.png 768w, https:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2-1536x687.png 1536w, https:\/\/mooney.gatech.edu\/security\/wp-content\/uploads\/2023\/07\/gridtrust_implementation2-2048x916.png 2048w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/a><\/p>\r\n<p>The GridTrust Server acts as the repository for software updates and software signatures. The server additionally verifies authentication of the GridTrust devices via the device&#8217;s PUF before issuing authorization for a software update to occur. The server itself is not connected to a PUF.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>This implementation guide will emphasize the implementation of the GridTrust server and GridTrust native device. The third component, the interfacing device (a feeder protection relay), has a nearly identical implementation to the native device.<\/p>\r\n<p><a href=\"https:\/\/github.com\/huttokd\/GridTrust\">GitHub Link<\/a><\/p>\r\n<h2><strong>Devices Utilized:<\/strong><\/h2>\r\n<p>Dell Precision 5560 Laptop<\/p>\r\n\r\n\r\n\r\n<h2><strong>Software Utilized:<\/strong><\/h2>\r\n<p>Operating System &#8211; Fedora 37<\/p>\r\n<p>Reverse Proxy &#8211; Nginx<\/p>\r\n<p>Docker \/ Docker-Compose<\/p>\r\n<p>Database &#8211; PostgreSQL<\/p>\r\n<h2><strong>Server Configuration<\/strong><\/h2>\r\n<p>We utilized Fedora 37 for our GridTrust implementation. Newer versions of Fedora and other Linux distributions should work provided there is support for the software packages used, but have not been tested by us.<\/p>\r\n<p>After installing Fedora onto the target computer, we require several software packages.<\/p>\r\n<p>1. Install Docker<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li><a href=\"https:\/\/docs.docker.com\/desktop\/install\/fedora\/\">https:\/\/docs.docker.com\/desktop\/install\/fedora\/<\/a><\/li>\r\n<li>On Fedora, you must separately install the <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">docker-compose <\/span>package using <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo dnf install docker-compose <\/span>(after completing the installations at the link above)<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>2. Install Rust<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li><a href=\"https:\/\/developer.fedoraproject.org\/tech\/languages\/rust\/rust-installation.html\">https:\/\/developer.fedoraproject.org\/tech\/languages\/rust\/rust-installation.html<\/a><\/li>\r\n<li>Install Rust with RustUp, per the link above<\/li>\r\n<li>Afterwards, your system may not have the required C compiler packages needed to compile and run Rust projects<\/li>\r\n<li>To install backend C compilation software: <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo dnf install make automake gcc gcc-c++ kernel-devel<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>3. Install OpenSSL: <span style=\"font-size: 10pt; font-family: 'courier new', courier, monospace;\">sudo dnf -y install openssl openssl-devel<\/span><\/p>\r\n<p>4. Create a PostgreSQL Container<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li><a href=\"https:\/\/tms-dev-blog.com\/postgresql-database-with-rust-how-to\/\">https:\/\/tms-dev-blog.com\/postgresql-database-with-rust-how-to\/<\/a>\r\n<ul>\r\n<li>Follow the steps to start the PostgreSQL container, don&#8217;t create a new Rust project<\/li>\r\n<li>Check the GitHub link for the provided configuration file (<span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">docker-compose.yaml<\/span>)<\/li>\r\n<li>In the tutorial, the Docker container is named <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">rust-postgresql-tutorial_postgres_1<\/span>, but the container using the configuration file on GitHub is named<span style=\"font-size: 10pt; font-family: courier new, courier, monospace;\"> server_postgres_1<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>5. Set Up PostgreSQL Database<\/p>\r\n<ol>\r\n<li style=\"list-style-type: none;\">\r\n<ol>\r\n<li style=\"list-style-type: none;\">\r\n<ol>\r\n<li>If applicable, start the Docker daemon: <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo systemctl start docker<\/span><\/li>\r\n<li>Log into the docker PostgreSQL container: <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo <\/span><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">docker exec -ti &lt;database name&gt; bash<\/span><\/li>\r\n<li>Enter the PostgreSQL terminal:<span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\"> \\psql<\/span><\/li>\r\n<li>If the above command does not work due to the error <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">failed: FATAL: role &#8220;root&#8221; does not exist<\/span>, then instead use the command <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">psql -d postgres -U dboperator<\/span><\/li>\r\n<\/ol>\r\n<\/li>\r\n<li>When setting up the database for the first time, run one-by-one the commands in the GitHub-provided file <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">sql_setup<\/span>\r\n<ul>\r\n<li>Edit AES keys and counter files as desired<\/li>\r\n<li>Note: after running the SQL setup commands, there will be 3 databases: <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">template0<\/span>, <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">template1<\/span>, and <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">postgres<\/span><\/li>\r\n<li>We are concerned with postgres only. Select this database by typing the PSQL command <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">\\c<\/span><span style=\"font-size: 10pt; font-family: courier new, courier, monospace;\"> postgres<\/span> and list the relations in the database with <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">\\d<\/span><\/li>\r\n<li>Should see a table named <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">substationdevices<\/span> and a sequence named <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">substationdevices_id_seq<\/span><\/li>\r\n<li>Verify the table contents are correct using the PSQL command <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">SELECT * FROM substationdevices;<\/span><\/li>\r\n<li>If you need to edit a value in the table, the example PSQL command is as follows: <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">UPDATE substationdevices SET key=&#8217;00000000000000000000000000000001&#8242; WHERE UUID=&#8217;1&#8242;;<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>Accessing the database in future runs after it has already been setup:\r\n<ol>\r\n<li>Ensure the Docker daemon is running: <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo systemctl start docker<\/span><\/li>\r\n<li>Enter the Docker container: <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo docker exec -ti server_postgres_1 bash<\/span><\/li>\r\n<li>Access the PostgreSQL database: <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">psql -d postgres -U dboperator<\/span><\/li>\r\n<li>Exit the database with <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">\\q <\/span>and exit the container with <span style=\"font-size: 10pt;\">exit<\/span><\/li>\r\n<\/ol>\r\n<\/li>\r\n<\/ol>\r\n<\/li>\r\n<\/ol>\r\n<p>6. The server file structure is as follows. This guide focuses on setting up GridTrust with the temperature sensor, so the files below pertain to the temperature sensor.<\/p>\r\n<p>By default, the paths are relative to the root directory of the Rust project created in step 8 (the same directory containing <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">Cargo.toml<\/span>).<\/p>\r\n<p>If these paths are changed, the Rust code must be changed to reflect that.<\/p>\r\n<p>These files are generated in subsequent steps:<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">.\/update_files\/tempsensor\/util_sign64.txt<\/span><\/li>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">.\/update_files\/tempsensor\/vendor_sign64.txt<\/span><\/li>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">.\/update_files\/tempsensor\/update64.txt<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>7. Set Up Reverse Proxy<\/p>\r\n<ol>\r\n<li style=\"list-style-type: none;\">\r\n<ol>\r\n<li><span style=\"color: #ff0000;\">**Important**: On Fedora, the SELinux policy for the server machine needs to be set to permissive. Otherwise, the Nginx server will not accept connections.<\/span>\r\n<ul>\r\n<li>Run <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">sudo setenforce 0<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>Install Nginx <br \/>\r\n<ul>\r\n<li>https:\/\/developer.fedoraproject.org\/start\/sw\/web-app\/nginx.html<\/li>\r\n<li>https:\/\/nginx.org\/en\/<\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>Replace the default Nginx configuration file, located at <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">\/etc\/nginx\/nginx.conf,<\/span> with the <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">\/server\/nginx.conf<\/span> file found in the GitHub repository<\/li>\r\n<li>Restart nginx\r\n<ol>\r\n<li><span style=\"font-size: 10pt; font-family: 'courier new', courier, monospace;\">sudo nginx -s reload<\/span><\/li>\r\n<\/ol>\r\n<\/li>\r\n<\/ol>\r\n<\/li>\r\n<\/ol>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li>Common errors:<\/li>\r\n<li><span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">[emerg] bind() to 0.0.0.0:3030 failed (13: Permission denied)<\/span>\r\n<ul>\r\n<li>The port 3030 is not in the SELinux allowed ports list. Resolve by running <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo semanage port -a -t http_port_t -p tcp 3030<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">[emerg] bind() to 0.0.0.0:3030 failed (98: Address already in use)<\/span><br \/>\r\n<ul>\r\n<li>Nginx is somehow running two instances. Forcibly kill Nginx by running <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo pkill -f nginx &amp; wait $!<\/span> and start by running <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo systemctl start nginx<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>8. Create a new Rust project<\/p>\r\n<ol>\r\n<li style=\"list-style-type: none;\">\r\n<ol>\r\n<li><span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">cargo new server<\/span><\/li>\r\n<li>Replace files in<span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\"> ..\/server\/src<\/span> with GitHub <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">\/server\/code\/src<\/span> files<\/li>\r\n<li>Replace<span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\"> ..\/server\/cargo.toml<\/span> with GitHub <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">server\/cargo.toml<\/span><\/li>\r\n<li><span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">cargo build<\/span><\/li>\r\n<\/ol>\r\n<\/li>\r\n<\/ol>\r\n<p>9. Using OpenSSL, create a self-signed certificate for Nginx and a pfx certificate for GridTrust devices<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li>The certificate files should be stored in <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">\/etc\/nginx\/ca.key<\/span> and <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">\/etc\/nginx\/ca.crt<\/span><\/li>\r\n<li>OpenSSL will prompt the user with questions for the certificate info, such as country name, state or province name, etc.\r\n<ul>\r\n<li><span style=\"color: #ff0000;\">When prompted with the &#8220;Common Name&#8221; option, specify the LAN IPv4 address of the server machine (in our case, we used the LAN IPv4 <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">172.23.2.200<\/span>)<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<li>Fill the other values out as desired, the ones besides &#8220;Common Name&#8221; do not matter from a technical standpoint<\/li>\r\n<li>Create a file at <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">\/etc\/keys\/keys.pass<\/span> and input (some) passphrase, for this guide, the passphrase will be <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">testpassword<\/span><\/li>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">sudo openssl req -x509 -days 365 -newkey rsa:2048 -keyout \/etc\/nginx\/ca.key -out \/etc\/nginx\/ca.crt -passout file:\/etc\/keys\/keys.pass<\/span><\/li>\r\n<li>Later on during the native device and interface setup, a <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">.pfx <\/span>file will be needed for authenticating the devices so that they can communicate over HTTPS<\/li>\r\n<li><span style=\"font-size: 10pt; font-family: 'courier new', courier, monospace;\">sudo openssl pkcs12 -export -out gridtrust.pfx -inkey ca.key -in ca.crt -passin file:\/etc\/keys\/keys.pass -passout pass:testpassword<\/span><\/li>\r\n<li>If the passphrase being used is not <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">testpassword<\/span>, edit the last part of the command as needed<\/li>\r\n<li>Ensure that <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">gridtrust.pfx<\/span> and <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">ca.crt <\/span>can be manually transferred off of the server machine; these files will be needed on the native device and interface machines<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>10. Using OpenSSL, create asymmetric key pairs for signing update files. If there are any permissions-related errors encountered, preface the commands with <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">sudo<\/span>:<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">openssl genpkey -algorithm RSA -out vendor.priv.pem -pkeyopt rsa_keygen_bits:2048<\/span><\/li>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">openssl rsa -pubout -in vendor.priv.pem -out vendor.pub.pem<\/span><\/li>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">openssl genpkey -algorithm RSA -out utility.priv.pem -pkeyopt rsa_keygen_bits:2048<\/span><\/li>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">openssl rsa -pubout -in utility.priv.pem -out utility.pub.pem<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>The public key files <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">utility.pub.pem <\/span>and <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">vendor.pub.pem<\/span> will need to be manually transferred to the native device computer in future steps.<\/p>\r\n<p>11. The update files can be obtained from GitHub at the paths <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">\/native\/updates\/celsius\/update64.txt<\/span><\/p>\r\n<p>and<\/p>\r\n<p><span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\"> \/native\/updates\/celsius\/update_bin.ino.hex<\/span>.<\/p>\r\n<p>The latter is just the former file decoded from base 64 (or vice versa, the former is the latter encoded in base64).<\/p>\r\n<p>This can be observed by decoding <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">update64.txt <\/span>and running a <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">diff <\/span>on the decoded <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">update64.txt <\/span>and<span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\"> update_bin.ino.hex<\/span><\/p>\r\n<p>Sign the update file using the utility and vendor keys from the previous step (so the server is acting as the utility).<\/p>\r\n<p>Note that we do not sign the base 64 update file, but the original binary update file. Then we convert the signatures to base 64.<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li><span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">openssl dgst -sha256 -sign utility.priv.pem -out utility_sig.txt update_bin.ino.hex<\/span><\/li>\r\n<li><span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">openssl dgst -sha256 -sign vendor.priv.pem -out vendor_sig.txt update_bin.ino.hex<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>Convert the utility signature to base 64:<\/p>\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li style=\"list-style-type: none;\">\r\n<ul>\r\n<li><span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">base64 utility_sig.txt &gt; util_sign64.txt<\/span><\/li>\r\n<li><span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">base64 vendor_sig.txt &gt; vendor_sign64.txt<\/span><\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<\/li>\r\n<\/ul>\r\n<p>In the case of local updates, <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">util_sign64.txt<\/span> and <span style=\"font-family: 'courier new', courier, monospace; font-size: 10pt;\">vendor_sign64.txt<\/span> will need to be manually transferred to the native device computer in future steps.<\/p>\r\n<p>The non-base64 signature (<span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">utility_sig.txt, vendor_sig.txt<\/span>) files are no longer needed and can be deleted.<\/p>\r\n<ol>\r\n<li style=\"list-style-type: none;\">Make sure to place <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">update64.txt<\/span>, <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">util_sign64.txt<\/span>, and <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">vendor_sign64.txt<\/span> in the <span style=\"font-family: courier new, courier, monospace; font-size: 10pt;\">\/update_files\/tempsensor <\/span>directory from step 6.<\/li>\r\n<\/ol>\r\n","protected":false},"excerpt":{"rendered":"<p>GridTrust Server Setup &nbsp; The GridTrust Server acts as the repository for software updates and software signatures. The server additionally verifies authentication of the GridTrust devices via the device&#8217;s PUF before issuing authorization for a software update to occur. The server itself is not connected to a PUF. &nbsp; This implementation guide will emphasize the &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/mooney.gatech.edu\/security\/gridtrust\/server\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;GridTrust Server Setup&#8221;<\/span><\/a><\/p>\n","protected":false},"author":5,"featured_media":0,"parent":351,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"folder":[12],"class_list":["post-377","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/pages\/377","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/comments?post=377"}],"version-history":[{"count":51,"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/pages\/377\/revisions"}],"predecessor-version":[{"id":647,"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/pages\/377\/revisions\/647"}],"up":[{"embeddable":true,"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/pages\/351"}],"wp:attachment":[{"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/media?parent=377"}],"wp:term":[{"taxonomy":"folder","embeddable":true,"href":"https:\/\/mooney.gatech.edu\/security\/wp-json\/wp\/v2\/folder?post=377"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}