<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sebastian Wirkijowski</title>
    <description>The latest articles on Forem by Sebastian Wirkijowski (@wirkijowski).</description>
    <link>https://forem.com/wirkijowski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2574503%2F6abbef70-4452-4530-b504-31053588f095.png</url>
      <title>Forem: Sebastian Wirkijowski</title>
      <link>https://forem.com/wirkijowski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wirkijowski"/>
    <language>en</language>
    <item>
      <title>How to attach extra data to a GraphQL response on Apollo Server</title>
      <dc:creator>Sebastian Wirkijowski</dc:creator>
      <pubDate>Fri, 20 Dec 2024 15:12:47 +0000</pubDate>
      <link>https://forem.com/wirkijowski/how-to-attach-extra-data-to-a-graphql-response-on-apollo-server-5227</link>
      <guid>https://forem.com/wirkijowski/how-to-attach-extra-data-to-a-graphql-response-on-apollo-server-5227</guid>
      <description>&lt;p&gt;Let's say that we want to include a unique request identifier to each GraphQL response.&lt;/p&gt;

&lt;p&gt;We can do that by adding a &lt;code&gt;requestId&lt;/code&gt; field to the Query type, then resolving that field to some unique identifier we set in the context for each request. This isn't a perfect solution though, since we have to include that field on every single request on our client and it slightly increases the size of the request sent to the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is a better way!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can create a small plugin (middleware) that attaches our custom data to the response body's &lt;code&gt;extensions&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlz5whbya83gu8h2bsfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwlz5whbya83gu8h2bsfz.png" alt="Apollo Sandbox - Screenshot" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on what the "&lt;a href="https://www.apollographql.com/docs/apollo-server/integrations/plugins" rel="noopener noreferrer"&gt;Creating Apollo Server Plugins&lt;/a&gt;" documentation page tells us, our plugin should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// extensionsPlugin.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extensionsPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;requestDidStart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;willSendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;singleResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;singleResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contextValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;};&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Feel free to use &lt;code&gt;console.log(requestContent.response)&lt;/code&gt; to learn how the data is structured.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Keep in mind that only the &lt;code&gt;extensions&lt;/code&gt; key of &lt;code&gt;body.singleResult&lt;/code&gt; will work out of the box, because it's part of the GraphQL standard. We cannot add &lt;code&gt;requestId&lt;/code&gt; directly to &lt;code&gt;body.singleResult&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We don't have to worry about any preexisting extensions because Apollo Server does not actually use any by itself. Things may get a bit more complicated if you are using multiple plugins that affect the extensions field. If that's the case, just add &lt;code&gt;...requestContext.response.body?.extensions,&lt;br&gt;
&lt;/code&gt; to your extensions object to include the original fields.&lt;/p&gt;

&lt;p&gt;And now we just have to implement it!&lt;/p&gt;

&lt;p&gt;This example uses the &lt;a href="https://github.com/ulid/javascript" rel="noopener noreferrer"&gt;ulid&lt;/a&gt; package to generate IDs that are compact and time-sortable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ulid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ulid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;extensionsPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./extensionsPlugin.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;extensionsPlugin&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;startStandaloneServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ulid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and thats it!&lt;/p&gt;

&lt;p&gt;Why does it work? Context is built for each request separately (contextual) and is always available to all resolvers handling the request. It's best to set all needed variables in context, because it's created before any plugin hooks are fired (e.g.: &lt;code&gt;requestDidStart&lt;/code&gt;). We add the &lt;code&gt;requestId&lt;/code&gt; to our context and make it available everywhere, then our plugin pulls it from the context and attaches it to the response body right before it's sent back.&lt;/p&gt;

&lt;p&gt;We can also use this to include some extra data, like time till current session expiration, request processing time or an identifier of a specific API server behind a gateway or load balancer.&lt;/p&gt;

&lt;p&gt;Got an idea on what else could we attach to our response? Do share in the comments :)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Setting up iptables for web apps</title>
      <dc:creator>Sebastian Wirkijowski</dc:creator>
      <pubDate>Mon, 16 Dec 2024 08:12:28 +0000</pubDate>
      <link>https://forem.com/wirkijowski/setting-up-iptables-for-web-apps-31hn</link>
      <guid>https://forem.com/wirkijowski/setting-up-iptables-for-web-apps-31hn</guid>
      <description>&lt;p&gt;The &lt;code&gt;iptables&lt;/code&gt; package gives us advanced, granular control over our firewall, with important built-in features like filtering and limiting. This guide will cover a more advanced approach, leveraging the mentioned capabilities, the included &lt;code&gt;conntrack&lt;/code&gt; module,  and restrictive rules, while exploring potential security risks.&lt;/p&gt;

&lt;p&gt;Every command we use in this guide (except &lt;code&gt;man&lt;/code&gt;) needs elevated privileges, so I have prepended all commands with &lt;code&gt;sudo&lt;/code&gt; to make copying everything — after careful checking, of course — easier.&lt;/p&gt;

&lt;p&gt;We will be implementing a firewall that follows the principles laid out by the stateful firewall concept.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Stateful_firewall" rel="noopener noreferrer"&gt;Wikipedia — Stateful firewall&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The configuration covered in this guide was tested on a local Ubuntu 24.04 virtual machine and on a cloud Ubuntu 22.04 virtual machine. Both with Apache2 installed and enabled for HTTP/HTTPS rule testing. Testing included making and receiving HTTP/HTTPS requests, checking &lt;code&gt;apt&lt;/code&gt; connectivity, using the &lt;code&gt;dig&lt;/code&gt; command and using &lt;code&gt;ping&lt;/code&gt; from client to virtual machine.&lt;/p&gt;

&lt;p&gt;This configuration is compatible with the LAMP, LEMP and MERN stacks, and should work with other similar stacks as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally, this was part of my “Setting up a LAMP (the stack)” guide, but I decided to split it into a separate publication to avoid overwhelming newcomers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wirkijowski.medium.com/setting-up-iptables-on-a-lamp-stack-331c893b5a41" rel="noopener noreferrer"&gt;This guide is also available on medium&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Manual and documentation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5izgw5df2sefnrtyjr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5izgw5df2sefnrtyjr0.png" alt="Terminal screenshot — iptables manual search preview" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You are encouraged to check out the documentation of commands we will be using in this guide. Linux command documentation and manuals can be accessed by using &lt;code&gt;man iptables&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use up and down arrow keys or &lt;code&gt;Page Up&lt;/code&gt;/&lt;code&gt;Page Down&lt;/code&gt; to navigate through the manual.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;/&lt;/code&gt; while the manual is open and type your search term to highlight instances of that term or phrase. This is especially significant for newcomers, since long documentation can get overwhelming really quick.&lt;/li&gt;
&lt;li&gt;To exit the manual, press &lt;code&gt;q&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some manuals also have examples on how to perform certain actions.&lt;/p&gt;

&lt;p&gt;Alternatively, you can use &lt;code&gt;iptables -h&lt;/code&gt; for summarized details and list of options.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do firewalls work?
&lt;/h2&gt;

&lt;p&gt;All firewall environments use the same or similar terminology. The concepts remain unchanged. When we set up firewall rules, we add those rules to a &lt;em&gt;chain&lt;/em&gt;, on a specific &lt;em&gt;table&lt;/em&gt;. There is no need to elaborate on tables at this point, since usually, and in this case, we will be only working on the default “filter” table. &lt;/p&gt;

&lt;p&gt;The usual chains are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;INPUT&lt;/strong&gt; — packets heading into our machine,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FORWARD&lt;/strong&gt; — packets passing through (mainly used by routers),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OUTPUT&lt;/strong&gt; — packets that our machine sends out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we are setting up a firewall for a web server machine, I’m assuming that you want to host a website or other web application. This means that we need to open ports &lt;code&gt;80&lt;/code&gt; (HTTP) and &lt;code&gt;443&lt;/code&gt; (HTTPS). On top of that, we need to open the SSH/SFTP port &lt;code&gt;22&lt;/code&gt; (SFTP is essentially FTP over SSH, which means it also uses port &lt;code&gt;22&lt;/code&gt;), to keep remote communication with our machine open. Those ports only use the TCP protocol. We will also allow limited ICMP (pings) traffic and DNS lookups (port &lt;code&gt;53&lt;/code&gt; via UDP).&lt;/p&gt;

&lt;p&gt;Remember, if you changed your SSH port, you have to use your custom port instead of the default &lt;code&gt;22&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;In a standard firewall solution, rules are processed in the same order in which they are added. In a command line interface (CLI), this is especially important. In a graphical user interface (GUI), usually you are allowed to move things around, but in both cases ordering mistakes can cause major problems, including complete lockouts.&lt;/p&gt;

&lt;p&gt;If you do lose remote access, most vendors that offer virtual machines also provide a web terminal or direct access via on-site support. If that’s not the case, don’t be ashamed to scrap the installation and try again from the start. It’s all part of the learning process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with &lt;code&gt;iptables&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;As you can probably see in the manual, there's a lot of commands, flags, and parameters going on. Let's go through some of the basic commands that will be helpful. We will skip the rule appending part, since we cover them in great detail later.&lt;/p&gt;

&lt;p&gt;To get current rules from all chains, with their corresponding position, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-vn&lt;/span&gt; &lt;span class="nt"&gt;--line-numbers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;-L&lt;/code&gt; parameter means “list”, and it lists all the rules in the selected chain. If no chain is specified, all chains will be displayed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-v&lt;/code&gt; parameter means “verbose”, this provides additional details about each rule.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-n&lt;/code&gt; parameter means “numeric”, this will prevent iptables from attempting to resolve hostnames and display IP addresses and ports in their numerical form.&lt;/li&gt;
&lt;li&gt;Finally, the &lt;code&gt;--line-numbers&lt;/code&gt; option adds a line number to each rule to the output. This is especially helpful when you need to delete or modify a specific rule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To delete a specific rule from a specific chain, you should look it up via the previous command, and then use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;chain&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;rule-id&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rule persistency
&lt;/h3&gt;

&lt;p&gt;Rules defined directly via &lt;code&gt;iptables&lt;/code&gt; are ephemeral, which means that they are temporary, and exist only for the duration of the session. If we reboot our server without saving them, they will disappear.&lt;/p&gt;

&lt;p&gt;Because of that, we also need the &lt;code&gt;iptables-persistent&lt;/code&gt; package on top of &lt;code&gt;iptables&lt;/code&gt;. It’s basically a &lt;code&gt;systemd&lt;/code&gt; service that loads your rules every time the machine starts.&lt;/p&gt;

&lt;p&gt;To save the configuration, we can use the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netfilter-persistent save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Danger:&lt;/strong&gt; Rules take effect immediately after you add them! This command only saves them to load them back after restart!&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Usually, you would want to set up INPUT and OUTPUT rules for specific ports, so your server can both receive and respond to requests from the internet. We will be taking it a bit farther.&lt;/p&gt;

&lt;p&gt;When someone visits our server, they send a request, by which they establish a connection. On the first request, the state is &lt;code&gt;NEW&lt;/code&gt;, but everything after that has the &lt;code&gt;ESTABLISHED&lt;/code&gt; or &lt;code&gt;RELATED&lt;/code&gt; (to existing connection; auxiliary connection) state. Of course this can differ on other protocols, but HTTP/HTTPS are not that fancy.&lt;/p&gt;

&lt;p&gt;We will be taking a balanced approach, by doing the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As a server — allow &lt;code&gt;NEW&lt;/code&gt; and &lt;code&gt;ESTABLISHED&lt;/code&gt; inbound &lt;code&gt;80, 443&lt;/code&gt; traffic destined for our web server, and allow outgoing packets only to &lt;code&gt;ESTABLISHED&lt;/code&gt; connections.&lt;/li&gt;
&lt;li&gt;As a client — allow &lt;code&gt;NEW&lt;/code&gt; and &lt;code&gt;ESTABLISHED&lt;/code&gt; outbound &lt;code&gt;80, 443&lt;/code&gt; traffic destined for external web servers (&lt;code&gt;apt&lt;/code&gt; usage, external APIs), and allow only &lt;code&gt;ESTABLISHED&lt;/code&gt; inbound connections.&lt;/li&gt;
&lt;li&gt;SSH (&lt;code&gt;22&lt;/code&gt;) — allow only &lt;code&gt;NEW&lt;/code&gt; or &lt;code&gt;ESTABLISHED&lt;/code&gt; inbound traffic and exclusively &lt;code&gt;ESTABLISHED&lt;/code&gt; outbound connections. This effectively prevents our server from making its own SSH connections.&lt;/li&gt;
&lt;li&gt;ICMP (protocol) &amp;amp; DNS (&lt;code&gt;53&lt;/code&gt; via UDP) — allow restricted ICMP and &lt;code&gt;ESTABLISHED&lt;/code&gt; inbound DNS, &lt;code&gt;NEW, ESTABLISHED&lt;/code&gt; outbound DNS. Since this is not a DNS server, we do not accept new or unrelated inbound DNS packets.&lt;/li&gt;
&lt;li&gt;and drop everything that does not match any of our rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup offers a solid foundation and limits the potential exposure surface while allowing fairly free standard HTTP/HTTPS traffic flow.&lt;/p&gt;

&lt;p&gt;Theoretically, you could also use this setup with the MERN stack if you're using NGINX as a reverse proxy. Since traffic between Node.js and NGINX is internal, it will flow smoothly over the loopback interface that will leave open.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security considerations
&lt;/h3&gt;

&lt;p&gt;The second you expose your server or machine to the internet, you will notice that there will always be someone or something scanning and probing it. Either by attempting to break in using popular insecure credentials, scanning open ports or poking your deployed web application for usual configuration mistakes or environment vulnerabilities.&lt;/p&gt;

&lt;p&gt;It's important to highlight, that security is not an objective or destination, rather a directive that guides our actions. You cannot achieve security, but you can do your best to mitigate malicious activity. Another thing to keep in mind for the future is: &lt;strong&gt;security through obscurity is not real security&lt;/strong&gt;. It’s a flawed security principle that focuses on secrecy instead of actual protection.&lt;/p&gt;

&lt;p&gt;In some cases ICMP can be used maliciously as a tunneling method. We will mitigate this by blocking all ICMP except for inbound &lt;code&gt;echo-request&lt;/code&gt; type messages and outgoing &lt;code&gt;echo-reply, destination-unreachable&lt;/code&gt; messages. This will allow external machines to ping ours (useful for basic health checks), but prevent our server to send any ICMP messages except the basic responses, and limits the possibility for an ICMP tunneling attack to work, but does not completely eradicate it. If you do not plan to use ICMP, feel free to not include those rules in your configuration or remove them later.&lt;/p&gt;

&lt;p&gt;Similar thing can be done with DNS, that’s why we are also limiting traffic on port &lt;code&gt;53&lt;/code&gt; UDP.&lt;/p&gt;

&lt;p&gt;On its own, the server should not be able to send outgoing requests without any safeguards. Opening outgoing communication raises the risk of data exfiltration instigated by viruses or code execution vulnerabilities. This configuration is a great foundation, but if you want to go the extra mile, after you install everything, you should delete the rules that allow outside connections as a client, and instead allow only outgoing client communication to specific IP addresses. We will cover this process at the end of this guide.&lt;/p&gt;

&lt;p&gt;When it comes to rate-limiting, I would advise against using the standard &lt;code&gt;iptables&lt;/code&gt; limit functionality, since this will not differentiate between legitimate connections and brute force attacks. Setting a rate limit that way even for packets with &lt;code&gt;NEW&lt;/code&gt; state, can result in lockout in a situation where you are attempting to connect during a brute-force attack or just regular vulnerability probing/scanning. You should use a specialized utility, like &lt;code&gt;fail2ban&lt;/code&gt; for that purpose instead.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Before we begin with the configuration, it’s crucial to understand that if an attacker has gained direct access to the server, our firewall will do absolutely nothing to stop them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If you do not have the &lt;code&gt;iptables&lt;/code&gt; installed, let’s install it with &lt;code&gt;apt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;iptables iptables-persistent
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;During the &lt;code&gt;iptables-persistent&lt;/code&gt; installation process, you will be asked to save and load the current &lt;code&gt;iptables&lt;/code&gt; rules for IPv4 and IPv6. At this point it is save to confirm both prompts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(Optional) If you don’t want to keep your previous rules and do not have the default action set to &lt;code&gt;DROP&lt;/code&gt; or &lt;code&gt;REJECT&lt;/code&gt; on input or output chains, remove all current rules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Danger!&lt;/strong&gt; This may cause loss of access to your machine!&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-F&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up the inbound rules. Feel free to paste this whole block directly:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 22 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; icmp &lt;span class="nt"&gt;--icmp-type&lt;/span&gt; echo-request &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; udp &lt;span class="nt"&gt;--sport&lt;/span&gt; 53 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Notice the &lt;code&gt;;&lt;/code&gt; operator, it allows for command chaining. With it, commands will execute even if the previous command fails. On top of that, we are using the &lt;code&gt;\&lt;/code&gt; multiline operator to make the command chain more legible.&lt;/p&gt;

&lt;p&gt;To break down what we just did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;-A&lt;/code&gt; flag means “append”, this tells the application that we want to add this rule at the end of a specific chain (&lt;code&gt;INPUT&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-p&lt;/code&gt; parameter means “protocol”, it defines which protocol is this rule for. All of our ports work on TCP, so we only allow packets via TCP.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--sport&lt;/code&gt; parameter can be found in the &lt;code&gt;iptables-extensions(8)&lt;/code&gt; manual, and defines which source (”s”) ports to allow.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--dport&lt;/code&gt; parameter defines the destination port of the packet.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-m&lt;/code&gt; parameter means “match”, in our case it matches the packet’s state to either &lt;code&gt;NEW&lt;/code&gt; or &lt;code&gt;ESTABLISHED&lt;/code&gt;. This will only allow new or already established connections to communicate with our machine. We’re using the &lt;code&gt;conntrack&lt;/code&gt; module, that’s why it’s &lt;code&gt;--ctstate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-j&lt;/code&gt; parameter means “jump”, and specifies what to do when the packet matches our rule. Here we want to accept everything.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up the outbound rules:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 22 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; icmp &lt;span class="nt"&gt;--icmp-type&lt;/span&gt; echo-reply &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; icmp &lt;span class="nt"&gt;--icmp-type&lt;/span&gt; destination-unreachable &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; udp &lt;span class="nt"&gt;--dport&lt;/span&gt; 53 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up rules that allow outside connections as a client:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;You will notice that those rules have swapped source, destination ports and connection states. That’s because when we are connecting to a remote server, our machine is acting as a client, so in the opposite way as with Apache.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allow internal (loopback) traffic:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-i&lt;/span&gt; lo &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-o&lt;/span&gt; lo &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Without those rules, your database will not be able to communicate with your web application, because we also set up a general &lt;code&gt;DROP&lt;/code&gt; rule further down the road.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Before setting the default DROP rules, check if your configuration contains the intended rules:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-vn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The parameters are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;-L&lt;/code&gt; parameter means “list”, and it lists all the rules in the selected chain. If no chain is specified, all chains will be displayed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-v&lt;/code&gt; parameter means “verbose”, this provides additional details about each rule.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-n&lt;/code&gt; parameter means “numeric”, this will prevent iptables from attempting to resolve hostnames and display IP addresses and ports in their numerical form.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output should looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjqqwd47dsq6ba6ja5qb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjqqwd47dsq6ba6ja5qb.png" alt="Terminal screenshot — active iptables rules" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change default behavior to drop all other packets:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Danger!&lt;/strong&gt; This may cause loss of access to your machine!&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-P&lt;/span&gt; INPUT DROP&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-P&lt;/span&gt; OUTPUT DROP&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-P&lt;/span&gt; FORWARD DROP
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will drop all traffic that do not match our previous rules and is a critical component of an effective firewall. Naturally we want to drop all FORWARD chain packets, since this is basically a resource provider and not a router. Under any circumstances there should be no forwarding going on, and if there is anything like that happening, it’s highly likely that there is some malicious activity going on, like tunneling.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save the the configuration:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netfilter-persistent save
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will first save the rules we just set up to a file and update the configuration.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Preventing unwanted outgoing HTTP/HTTPS
&lt;/h2&gt;

&lt;p&gt;Following up on the idea that the server itself should not be making new connections on itself (acting as a client), we should disable the potentially unsafe rules to lock our server down, while allowing the bare minimum it needs to serve web content.&lt;/p&gt;

&lt;p&gt;As mentioned before, if you are calling any external APIs on the server side, which means in your PHP code (or on your server-side rendered JavaScript app), you will have to leave those rules in to avoid connectivity problems. Removing them will also prevent all packages that pull external data to your machine from working, most importantly &lt;code&gt;apt/apt-get&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately, at least for administrative purposes, there are solutions. The ones that I personally know of are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Port_knocking" rel="noopener noreferrer"&gt;Port Knocking&lt;/a&gt; — this relies on sending a sequence of “knock” requests to the server, after which the server will: open up a port only for the IP address making the knocks (SSH) and set up rules that allow client HTTP/HTTPS requests. You can learn more from the &lt;a href="https://cs.fyi/guide/port-knocking-explained" rel="noopener noreferrer"&gt;Practical Guide to Port Knocking&lt;/a&gt; article available on cs.fyi.&lt;/li&gt;
&lt;li&gt;Scripting — make a script that appends allow client HTTP/HTTPS rules after you log in, and deletes them after you’re done with your tasks. This can be automated, but can also be done manually.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In all cases there will be some overhead.&lt;/p&gt;

&lt;p&gt;Use this code snippet to delete the client rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-D&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-D&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-D&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-D&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this snippet to put them back in when you need them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--sport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-A&lt;/span&gt; OUTPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 443 &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; NEW,ESTABLISHED &lt;span class="nt"&gt;-j&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Congratulations! You have improved your server’s security.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’ve &lt;strong&gt;hardened&lt;/strong&gt; the system by implementing a robust firewall.&lt;/li&gt;
&lt;li&gt;You’ve &lt;strong&gt;gained fundamental&lt;/strong&gt; knowledge of how firewalls work.&lt;/li&gt;
&lt;li&gt;You’ve also gained insights into both general and specific security concerns (like tunneling, exfiltration, rate-limitting)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spotted a mistake? Let me know!&lt;/p&gt;

&lt;p&gt;Thank you for reading. Hopefully this guide provided some value :)&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>linux</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
