<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cloudflare &#8211; hackanooga</title>
	<atom:link href="/category/cloudflare/feed/" rel="self" type="application/rss+xml" />
	<link>/</link>
	<description>Confessions of a homelab hacker</description>
	<lastBuildDate>Mon, 16 Sep 2024 13:07:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>/wp-content/uploads/2024/03/cropped-cropped-avatar-32x32.png</url>
	<title>Cloudflare &#8211; hackanooga</title>
	<link>/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Hardening your web server by only allowing traffic from Cloudflare</title>
		<link>/hardening-your-web-server-by-only-allowing-traffic-from-cloudflare/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Thu, 01 Aug 2024 21:02:29 +0000</pubDate>
				<category><![CDATA[Cloudflare]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Software Engineering]]></category>
		<category><![CDATA[Blog Post]]></category>
		<guid isPermaLink="false">/?p=607</guid>

					<description><![CDATA[Harden your origin server by only allowing access from Cloudflare IP addresses.]]></description>
										<content:encoded><![CDATA[
<h4 class="wp-block-heading">TDLR:</h4>



<p>If you just want the code you can find a convenient script on my Gitea server <a href="https://git.hackanooga.com/mikeconrad/random_scripts/src/branch/master/allow_only_cloudflare_traffic.sh" data-type="link" data-id="https://git.hackanooga.com/mikeconrad/random_scripts/src/branch/master/allow_only_cloudflare_traffic.sh">here</a>.  This version has been slightly modified so that it will work on more systems.</p>



<p></p>



<p>I have been using Cloudflare for several years for both personal and professional projects.  The free plan has some various gracious limits and it&#8217;s a great way to clear out some low hanging fruit and improve the security of your application.  If you&#8217;re not familiar with how it works, basically Cloudflare has two modes for DNS records.  <code>DNS Only</code> and <code>Proxied</code>.  The only way to get the advantages of Cloudflare is to use <code>Proxied</code> mode.  Cloudflare has some great documentation on how all of their services work but basically what happens is that you are pointing your domain to Cloudflare and Cloudflare provisions their network of Proxy servers to handle requests for your domain.</p>



<p>These proxy servers allow you to secure your domain by implementing things like WAF and Rate limiting.  You can also enforce HTTPS only mode and modify/add custom request/response headers.  You will notice that once you turn this mode on, your webserver will log requests as coming from Cloudflare IP addresses.  They have great <a href="https://developers.cloudflare.com/support/troubleshooting/restoring-visitor-ips/restoring-original-visitor-ips/" data-type="link" data-id="https://developers.cloudflare.com/support/troubleshooting/restoring-visitor-ips/restoring-original-visitor-ips/">documentation</a> on how to configure your webserver to restore these IP addresses in your log files.  </p>



<p>This is a very easy step to start securing your origin server but it still allows attackers to access your servers directly if they know the IP address.  We can take our security one step forward by only allowing requests from IP addresses originating within Cloudflare meaning that we will only allow requests if they are coming from a Cloudflare proxy server.  The setup is fairly straightforward.  In this example I will be using a Linux server.</p>



<p>We can achieve this pretty easily because Cloudflare provides a sort of API where they regular publish their network blocks.  Here is the basic script we will use:</p>



<pre class="wp-block-code"><code>for ip in $(curl https://www.cloudflare.com/ips-v4/); do iptables -I INPUT -p tcp -m multiport --dports http,https -s $ip -j ACCEPT; done

for ip in $(curl https://www.cloudflare.com/ips-v6/); do ip6tables -I INPUT -p tcp -m multiport --dports http,https -s $ip -j ACCEPT; done

iptables -A INPUT -p tcp -m multiport --dports http,https -j DROP
ip6tables -A INPUT -p tcp -m multiport --dports http,https -j DROP
</code></pre>



<p>This will pull down the latest network addresses from Cloudflare and create <code>iptables</code> rules for us.  These IP addresses do change from time to time so you may want to put this in a script and run it via a <code>cronjob</code> to have it update on a regular basis.</p>



<p>Now with this in place, here is the results:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="320" src="/wp-content/uploads/2024/09/image-1024x320.png" alt="" class="wp-image-608" srcset="/wp-content/uploads/2024/09/image-1024x320.png 1024w, /wp-content/uploads/2024/09/image-300x94.png 300w, /wp-content/uploads/2024/09/image-768x240.png 768w, /wp-content/uploads/2024/09/image.png 1173w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>This should cut down on some of the noise from attackers and script kiddies trying to find holes in your security.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SFTP Server Setup for Daily Inventory File Transfers</title>
		<link>/sftp-server-setup-for-daily-inventory-file-transfers/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Wed, 17 Jul 2024 02:15:23 +0000</pubDate>
				<category><![CDATA[Cloudflare]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Self Hosted]]></category>
		<category><![CDATA[Upwork Project]]></category>
		<guid isPermaLink="false">/?p=599</guid>

					<description><![CDATA[Job Description We are looking for an experienced professional to help us set up an SFTP server that will allow our vendors to send us inventory files on a daily basis. The server should ensure secure and reliable file transfers, allowing our vendors to easily upload their inventory updates. The successful candidate will possess expertise [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading">Job Description</h3>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>We are looking for an experienced professional to help us set up an SFTP server that will allow our vendors to send us inventory files on a daily basis. The server should ensure secure and reliable file transfers, allowing our vendors to easily upload their inventory updates. The successful candidate will possess expertise in SFTP server setup and configuration, as well as knowledge of network security protocols. The required skills for this job include:</p>



<p>&#8211; SFTP server setup and configuration<br>&#8211; Network security protocols<br>&#8211; Troubleshooting and problem-solving skills</p>



<p>If you have demonstrated experience in setting up SFTP servers and ensuring smooth daily file transfers, we would love to hear from you.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p></p>



<h3 class="wp-block-heading">My Role</h3>



<p>I walked the client through the process of setting up a Digital Ocean account.  I created a <code>Ubuntu 22.04</code> VM and installed <a href="https://github.com/drakkan/sftpgo">SFTPGo</a>.  I set the client up with an administrator user so that they could easily login and manage users and shares.  I implemented some basic security practices as well and set the client up with a custom domain and free TLS/SSL certificate from LetsEncrypt.  With the documentation and screenshots I provided the client, they were able to get everything up and running and add users and connect other systems easily and securly.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p></p>



<h2 class="wp-block-heading">Client Feedback</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Rating is 5 out of 5.</p>



<p>Michael was EXTREMELY helpful and great to work with. We really benefited from his support and help with everything.</p>
</blockquote>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Fun with bots &#8211; SSH tarpitting</title>
		<link>/fun-with-bots-ssh-tarpitting/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Mon, 24 Jun 2024 13:37:43 +0000</pubDate>
				<category><![CDATA[Cloudflare]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[Blog Post]]></category>
		<guid isPermaLink="false">/?p=576</guid>

					<description><![CDATA[For those of you who aren&#8217;t familiar with the concept of a network tarpit it is a fairly simple concept. Wikipedia defines it like this: A tarpit is a service on a computer system (usually a server) that purposely delays incoming connections. The technique was developed as a defense against a computer worm, and the idea is that network abuses such as spamming or broad [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>For those of you who aren&#8217;t familiar with the concept of a network tarpit it is a fairly simple concept.  Wikipedia defines it like this:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>A <strong>tarpit</strong> is a service on a <a href="https://en.wikipedia.org/wiki/Computer_system">computer system</a> (usually a <a href="https://en.wikipedia.org/wiki/Server_(computing)">server</a>) that purposely delays incoming connections. The technique was developed as a defense against a <a href="https://en.wikipedia.org/wiki/Computer_worm">computer worm</a>, and the idea is that <a href="https://en.wikipedia.org/wiki/Computer_network">network</a> abuses such as <a href="https://en.wikipedia.org/wiki/Spamming">spamming</a> or broad scanning are less effective, and therefore less attractive, if they take too long. The concept is analogous with a <a href="https://en.wikipedia.org/wiki/Tar_pit">tar pit</a>, in which animals can get bogged down and slowly sink under the surface, like in a <a href="https://en.wikipedia.org/wiki/Swamp">swamp</a>.</p>
<cite><a href="https://en.wikipedia.org/wiki/Tarpit_(networking)">https://en.wikipedia.org/wiki/Tarpit_(networking)</a></cite></blockquote>



<p>If you run any sort of service on the internet then you know as soon as your server has a public IP address and open ports, there are scanners and bots trying to get in constantly.  If you take decent steps towards security then it is little more than an annoyance, but annoying all the less.  One day when I had some extra time on my hands I started researching ways to mess with the bots trying to scan/attack my site.</p>



<p>It turns out that this problem has been solved multiple times in multiple ways.  One of the most popular tools for tarpitting ssh connections is <a href="https://github.com/skeeto/endlessh">endlessh</a>.  The way it works is actually pretty simple.  The <a href="https://datatracker.ietf.org/doc/html/rfc4253#section-4.2">SSH RFC</a> states that when an SSH connection is established, both sides MUST send an identification string.  Further down the spec is the line that allows this behavior:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<pre class="wp-block-preformatted">   The server MAY send other lines of data before sending the version
   string.  Each line SHOULD be terminated by a Carriage Return and Line
   Feed.  Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
   in ISO-10646 UTF-8 [<a href="https://datatracker.ietf.org/doc/html/rfc3629">RFC3629</a>] (language is not specified).  Clients
   MUST be able to process such lines.  Such lines MAY be silently
   ignored, or MAY be displayed to the client user.  If they are
   displayed, control character filtering, as discussed in [<a href="https://datatracker.ietf.org/doc/html/rfc4253#ref-SSH-ARCH">SSH-ARCH</a>],
   SHOULD be used.  The primary use of this feature is to allow TCP-
   wrappers to display an error message before disconnecting.</pre>
<cite>SSH RFC</cite></blockquote>



<p>Essentially this means that their is no limit to the amount of data that a server can send back to the client and the client must be able to wait and process all of this data.  Now let&#8217;s see it in action.</p>



<pre class="wp-block-code"><code>git clone https://github.com/skeeto/endlessh.git
cd endlessh
make
./endlessh &amp;</code></pre>



<p>By default this fake server listens on port 2222.  I have a port forward set up that forwards all ssh traffic from port 22 to 2222.  Now try to connect via ssh:</p>



<pre class="wp-block-code"><code>ssh -vvv localhost -p 2222</code></pre>



<p>If you wait a few seconds you will see the server send back the version string and then start sending a random banner:</p>



<pre class="wp-block-code"><code>$:/tmp/endlessh$ 2024-06-24T13:05:59.488Z Port 2222
2024-06-24T13:05:59.488Z Delay 10000
2024-06-24T13:05:59.488Z MaxLineLength 32
2024-06-24T13:05:59.488Z MaxClients 4096
2024-06-24T13:05:59.488Z BindFamily IPv4 Mapped IPv6
2024-06-24T13:05:59.488Z socket() = 3
2024-06-24T13:05:59.488Z setsockopt(3, SO_REUSEADDR, true) = 0
2024-06-24T13:05:59.488Z setsockopt(3, IPV6_V6ONLY, true) = 0
2024-06-24T13:05:59.488Z bind(3, port=2222) = 0
2024-06-24T13:05:59.488Z listen(3) = 0
2024-06-24T13:05:59.488Z poll(1, -1)
ssh -vvv localhost -p 2222
OpenSSH_8.9p1 Ubuntu-3ubuntu0.7, OpenSSL 3.0.2 15 Mar 2022
debug1: Reading configuration data /home/mikeconrad/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/mikeconrad/.ssh/known_hosts'
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/mikeconrad/.ssh/known_hosts2'
debug2: resolving "localhost" port 2222
debug3: resolve_host: lookup localhost:2222
debug3: ssh_connect_direct: entering
debug1: Connecting to localhost &#91;::1] port 2222.
debug3: set_sock_tos: set socket 3 IPV6_TCLASS 0x10
debug1: Connection established.
2024-06-24T13:06:08.635Z = 1
2024-06-24T13:06:08.635Z accept() = 4
2024-06-24T13:06:08.635Z setsockopt(4, SO_RCVBUF, 1) = 0
2024-06-24T13:06:08.635Z ACCEPT host=::1 port=43696 fd=4 n=1/4096
2024-06-24T13:06:08.635Z poll(1, 10000)
debug1: identity file /home/mikeconrad/.ssh/id_rsa type 0
debug1: identity file /home/mikeconrad/.ssh/id_rsa-cert type 4
debug1: identity file /home/mikeconrad/.ssh/id_ecdsa type -1
debug1: identity file /home/mikeconrad/.ssh/id_ecdsa-cert type -1
debug1: identity file /home/mikeconrad/.ssh/id_ecdsa_sk type -1
debug1: identity file /home/mikeconrad/.ssh/id_ecdsa_sk-cert type -1
debug1: identity file /home/mikeconrad/.ssh/id_ed25519 type -1
debug1: identity file /home/mikeconrad/.ssh/id_ed25519-cert type -1
debug1: identity file /home/mikeconrad/.ssh/id_ed25519_sk type -1
debug1: identity file /home/mikeconrad/.ssh/id_ed25519_sk-cert type -1
debug1: identity file /home/mikeconrad/.ssh/id_xmss type -1
debug1: identity file /home/mikeconrad/.ssh/id_xmss-cert type -1
debug1: identity file /home/mikeconrad/.ssh/id_dsa type -1
debug1: identity file /home/mikeconrad/.ssh/id_dsa-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7
2024-06-24T13:06:18.684Z = 0
2024-06-24T13:06:18.684Z write(4) = 3
2024-06-24T13:06:18.684Z poll(1, 10000)
debug1: kex_exchange_identification: banner line 0: V
2024-06-24T13:06:28.734Z = 0
2024-06-24T13:06:28.734Z write(4) = 25
2024-06-24T13:06:28.734Z poll(1, 10000)
debug1: kex_exchange_identification: banner line 1: 2I=ED}PZ,z T_Y|Yc]$b{R]

</code></pre>



<p>This is a great way to give back to those bots and script kiddies.  In my research into other methods I also stumbled across this brilliant program <a href="https://github.com/iBug/fakessh">fakessh</a>.  While fakessh isn&#8217;t technically a tarpit, it&#8217;s more of a honeypot but very interesting nonetheless.  It creates a fake SSH server and logs the ip address, connection string and any commands executed by the attacker.  Essentially it allows any username/password combination to connect and gives them a fake shell prompt.  There is no actual access to any file system and all of their commands basically return gibberish.</p>



<p>Here are some logs from an actual server of mine running fakessh</p>



<pre class="wp-block-code"><code>2024/06/24 06:51:20 &#91;conn] ip=183.81.169.238:40430
2024/06/24 06:51:22 &#91;auth] ip=183.81.169.238:40430 version="SSH-2.0-Go" user="root" password="0"
2024/06/24 06:51:23 &#91;conn] ip=183.81.169.238:40444
2024/06/24 06:51:25 &#91;auth] ip=183.81.169.238:40444 version="SSH-2.0-Go" user="root" password="eve"
2024/06/24 06:51:26 &#91;conn] ip=183.81.169.238:48408
2024/06/24 06:51:27 &#91;auth] ip=183.81.169.238:48408 version="SSH-2.0-Go" user="root" password="root"
2024/06/24 06:51:28 &#91;conn] ip=183.81.169.238:48434
2024/06/24 06:51:30 &#91;auth] ip=183.81.169.238:48434 version="SSH-2.0-Go" user="root" password="1"
2024/06/24 06:51:30 &#91;conn] ip=183.81.169.238:48448
2024/06/24 06:51:32 &#91;auth] ip=183.81.169.238:48448 version="SSH-2.0-Go" user="root" password="123"
2024/06/24 06:51:32 &#91;conn] ip=183.81.169.238:48476
2024/06/24 06:51:35 &#91;auth] ip=183.81.169.238:48476 version="SSH-2.0-Go" user="root" password="admin"
2024/06/24 06:51:35 &#91;conn] ip=183.81.169.238:39250
2024/06/24 06:51:37 &#91;auth] ip=183.81.169.238:39250 version="SSH-2.0-Go" user="root" password="123456"
2024/06/24 06:51:38 &#91;conn] ip=183.81.169.238:39276
2024/06/24 06:51:40 &#91;auth] ip=183.81.169.238:39276 version="SSH-2.0-Go" user="root" password="123123"
2024/06/24 06:51:40 &#91;conn] ip=183.81.169.238:39294
2024/06/24 06:51:42 &#91;auth] ip=183.81.169.238:39294 version="SSH-2.0-Go" user="root" password="test"
2024/06/24 06:51:43 &#91;conn] ip=183.81.169.238:39316
2024/06/24 06:51:45 &#91;auth] ip=183.81.169.238:39316 version="SSH-2.0-Go" user="root" password="123456789"
2024/06/24 06:51:45 &#91;conn] ip=183.81.169.238:35108
2024/06/24 06:51:47 &#91;auth] ip=183.81.169.238:35108 version="SSH-2.0-Go" user="root" password="12345"
2024/06/24 06:51:48 &#91;conn] ip=183.81.169.238:35114
2024/06/24 06:51:50 &#91;auth] ip=183.81.169.238:35114 version="SSH-2.0-Go" user="root" password="password"
2024/06/24 06:51:50 &#91;conn] ip=183.81.169.238:35130
2024/06/24 06:51:52 &#91;auth] ip=183.81.169.238:35130 version="SSH-2.0-Go" user="root" password="12345678"
2024/06/24 06:51:52 &#91;conn] ip=183.81.169.238:35146
2024/06/24 06:51:54 &#91;auth] ip=183.81.169.238:35146 version="SSH-2.0-Go" user="root" password="111111"
2024/06/24 06:51:55 &#91;conn] ip=183.81.169.238:58490
2024/06/24 06:51:57 &#91;auth] ip=183.81.169.238:58490 version="SSH-2.0-Go" user="root" password="1234567890"
2024/06/24 06:51:57 &#91;conn] ip=183.81.169.238:58528
2024/06/24 06:51:59 &#91;auth] ip=183.81.169.238:58528 version="SSH-2.0-Go" user="root" password="1234"
2024/06/24 06:52:00 &#91;conn] ip=183.81.169.238:58572
2024/06/24 06:52:02 &#91;auth] ip=183.81.169.238:58572 version="SSH-2.0-Go" user="root" password="password123"
2024/06/24 06:52:02 &#91;conn] ip=183.81.169.238:58588
2024/06/24 06:52:04 &#91;auth] ip=183.81.169.238:58588 version="SSH-2.0-Go" user="root" password="ubuntu"
2024/06/24 06:52:05 &#91;conn] ip=183.81.169.238:37198
2024/06/24 06:52:07 &#91;auth] ip=183.81.169.238:37198 version="SSH-2.0-Go" user="Antminer" password="root"
2024/06/24 06:52:07 &#91;conn] ip=183.81.169.238:37214
2024/06/24 06:52:09 &#91;auth] ip=183.81.169.238:37214 version="SSH-2.0-Go" user="Antminer" password="admin"
2024/06/24 06:52:10 &#91;conn] ip=183.81.169.238:37238
2024/06/24 06:52:11 &#91;auth] ip=183.81.169.238:37238 version="SSH-2.0-Go" user="root" password="innot1t2"
2024/06/24 06:52:12 &#91;conn] ip=183.81.169.238:37258
2024/06/24 06:52:14 &#91;auth] ip=183.81.169.238:37258 version="SSH-2.0-Go" user="root" password="t1t2t3a5"
2024/06/24 06:52:14 &#91;conn] ip=183.81.169.238:55658
2024/06/24 06:52:16 &#91;auth] ip=183.81.169.238:55658 version="SSH-2.0-Go" user="root" password="blacksheepwall"
2024/06/24 06:52:17 &#91;conn] ip=183.81.169.238:55670
2024/06/24 06:52:19 &#91;auth] ip=183.81.169.238:55670 version="SSH-2.0-Go" user="root" password="envision"
2024/06/24 06:52:19 &#91;conn] ip=183.81.169.238:55708
2024/06/24 06:52:21 &#91;auth] ip=183.81.169.238:55708 version="SSH-2.0-Go" user="root" password="bwcon"
2024/06/24 06:52:22 &#91;conn] ip=183.81.169.238:55776
2024/06/24 06:52:23 &#91;auth] ip=183.81.169.238:55776 version="SSH-2.0-Go" user="admin" password="root"
2024/06/24 06:52:24 &#91;conn] ip=183.81.169.238:46646
2024/06/24 06:52:26 &#91;auth] ip=183.81.169.238:46646 version="SSH-2.0-Go" user="baikal" password="baikal"
2024/06/24 06:52:26 &#91;conn] ip=180.101.88.197:44620
2024/06/24 06:52:27 &#91;conn] ip=180.101.88.197:44620 err="ssh: disconnect, reason 11: "
2024/06/24 06:53:35 &#91;conn] ip=218.92.0.76:50610
2024/06/24 06:53:36 &#91;conn] ip=218.92.0.76:50610 err="ssh: disconnect, reason 11: "
2024/06/24 07:02:28 &#91;conn] ip=218.92.0.27:64676
2024/06/24 07:02:30 &#91;conn] ip=218.92.0.27:64676 err="ssh: disconnect, reason 11: "
2024/06/24 07:10:05 &#91;conn] ip=218.92.0.76:57601
2024/06/24 07:10:07 &#91;conn] ip=218.92.0.76:57601 err="ssh: disconnect, reason 11: "
2024/06/24 07:14:05 &#91;conn] ip=193.201.9.156:63056
2024/06/24 07:14:05 &#91;auth] ip=193.201.9.156:63056 version="SSH-2.0-Go" user="ubnt" password="ubnt"
2024/06/24 07:14:05 &#91;conn] ip=193.201.9.156:63056 err="read tcp 10.10.10.107:2222->193.201.9.156:63056: read: connection reset by peer"
2024/06/24 07:24:53 &#91;conn] ip=218.92.0.31:25485
2024/06/24 07:24:54 &#91;conn] ip=218.92.0.31:25485 err="ssh: disconnect, reason 11: "
2024/06/24 07:24:54 &#91;conn] ip=218.92.0.112:39270
2024/06/24 07:24:56 &#91;conn] ip=218.92.0.112:39270 err="ssh: disconnect, reason 11: "
2024/06/24 07:26:42 &#91;conn] ip=218.92.0.34:59993
2024/06/24 07:35:46 &#91;conn] ip=218.92.0.34:59993 err="read tcp 10.10.10.107:2222->218.92.0.34:59993: read: connection reset by peer"
2024/06/24 07:41:28 &#91;conn] ip=218.92.0.107:62285
2024/06/24 07:41:31 &#91;conn] ip=218.92.0.107:62285 err="ssh: disconnect, reason 11: "
2024/06/24 07:43:27 &#91;conn] ip=218.92.0.29:34556
2024/06/24 07:43:28 &#91;conn] ip=218.92.0.29:34556 err="ssh: disconnect, reason 11: "
2024/06/24 07:44:15 &#91;conn] ip=218.92.0.118:37047
2024/06/24 07:44:22 &#91;conn] ip=218.92.0.118:37047 err="ssh: disconnect, reason 11: "
2024/06/24 07:56:10 &#91;conn] ip=157.245.98.245:6116
2024/06/24 07:56:11 &#91;conn] ip=157.245.98.245:6116 err="ssh: unexpected message type 20 (expected 21)"
2024/06/24 07:57:57 &#91;conn] ip=218.92.0.112:28326
2024/06/24 07:57:58 &#91;conn] ip=218.92.0.112:28326 err="ssh: disconnect, reason 11: "
2024/06/24 08:00:01 &#91;conn] ip=218.92.0.24:24948
2024/06/24 08:00:02 &#91;conn] ip=218.92.0.24:24948 err="ssh: disconnect, reason 11: "
2024/06/24 08:06:19 &#91;conn] ip=193.201.9.156:46865
2024/06/24 08:06:20 &#91;auth] ip=193.201.9.156:46865 version="SSH-2.0-Go" user="root" password="xc3511"
2024/06/24 08:06:20 &#91;conn] ip=193.201.9.156:46865 err="read tcp 10.10.10.107:2222->193.201.9.156:46865: read: connection reset by peer"
2024/06/24 08:14:26 &#91;conn] ip=180.101.88.197:48347
2024/06/24 08:14:28 &#91;conn] ip=180.101.88.197:48347 err="ssh: disconnect, reason 11: "
2024/06/24 08:16:28 &#91;conn] ip=218.92.0.56:18064
2024/06/24 08:16:32 &#91;conn] ip=218.92.0.56:18064 err="ssh: disconnect, reason 11: "
2024/06/24 08:30:55 &#91;conn] ip=180.101.88.196:40495
2024/06/24 08:30:57 &#91;conn] ip=180.101.88.196:40495 err="ssh: disconnect, reason 11: "
2024/06/24 08:32:20 &#91;conn] ip=85.209.11.227:15493
2024/06/24 08:32:21 &#91;auth] ip=85.209.11.227:15493 version="SSH-2.0-Go" user="telecomadmin" password="admintelecom"
2024/06/24 08:32:21 &#91;conn] ip=85.209.11.227:15493 err="read tcp 10.10.10.107:2222->85.209.11.227:15493: read: connection reset by peer"
2024/06/24 08:33:19 &#91;conn] ip=218.92.0.34:59804
2024/06/24 08:33:21 &#91;conn] ip=218.92.0.34:59804 err="ssh: disconnect, reason 11: "
2024/06/24 08:41:00 &#91;conn] ip=218.92.0.27:45567
2024/06/24 08:41:02 &#91;conn] ip=218.92.0.27:45567 err="ssh: disconnect, reason 11: "
2024/06/24 08:47:15 &#91;conn] ip=180.101.88.196:17032
2024/06/24 08:47:16 &#91;conn] ip=180.101.88.196:17032 err="ssh: disconnect, reason 11: "
2024/06/24 08:49:51 &#91;conn] ip=218.92.0.29:26360
2024/06/24 08:49:57 &#91;conn] ip=218.92.0.29:26360 err="ssh: disconnect, reason 11: "
2024/06/24 08:58:27 &#91;conn] ip=193.201.9.156:49525
2024/06/24 08:58:28 &#91;auth] ip=193.201.9.156:49525 version="SSH-2.0-Go" user="admin" password="1234"
2024/06/24 08:58:28 &#91;conn] ip=193.201.9.156:49525 err="read tcp 10.10.10.107:2222->193.201.9.156:49525: read: connection reset by peer"
2024/06/24 08:58:44 &#91;conn] ip=218.92.0.31:11835
2024/06/24 08:58:46 &#91;conn] ip=218.92.0.31:11835 err="ssh: disconnect, reason 11: "
2024/06/24 09:03:38 &#91;conn] ip=218.92.0.107:57758
2024/06/24 09:03:40 &#91;conn] ip=218.92.0.107:57758 err="ssh: disconnect, reason 11: "
2024/06/24 09:07:36 &#91;conn] ip=218.92.0.56:21354
2024/06/24 09:07:39 &#91;conn] ip=218.92.0.56:21354 err="ssh: disconnect, reason 11: "
</code></pre>



<p>Those are mostly connections and disconnections.  They probably connected, realized it was fake and disconnected.  There are a couple that tried to execute some commands though:</p>



<pre class="wp-block-code"><code>:~$ sudo grep head /var/log/fakessh/fakessh.log 
2024/06/23 15:48:02 &#91;shell] ip=184.160.233.163:45735 duration=0s bytes=15 head="ls 2>/dev/null\n"
2024/06/24 03:55:11 &#91;shell] ip=14.46.116.243:43656 duration=20s bytes=0 head=""
</code></pre>



<p>Fun fact: Cloudflare&#8217;s Bot Fight Mode uses a form of tarpitting:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Once enabled, when we detect a bad bot, we will do three things: (1) we’re going to disincentivize the bot maker economically by tarpitting them, including requiring them to solve a computationally intensive challenge that will require more of their bot’s CPU; (2) for <a href="https://blog.cloudflare.com/bandwidth-alliance/">Bandwidth Alliance partners</a>, we’re going to hand the IP of the bot to the partner and get the bot kicked offline; and (3) we’re going to plant trees to make up for the bot’s carbon cost.</p>
<cite><a href="https://blog.cloudflare.com/cleaning-up-bad-bots">https://blog.cloudflare.com/cleaning-up-bad-bots</a></cite></blockquote>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Traefik with Let’s Encrypt and Cloudflare (pt 2)</title>
		<link>/traefik-with-lets-encrypt-and-cloudflare-pt-2/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Thu, 15 Feb 2024 20:19:12 +0000</pubDate>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[Cloudflare]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Traefik]]></category>
		<category><![CDATA[Blog Post]]></category>
		<guid isPermaLink="false">https://wordpress.hackanooga.com/?p=425</guid>

					<description><![CDATA[In this article we are gonna get into setting up Traefik to request dynamic certs from Lets Encrypt. I had a few issues getting this up and running and the documentation is a little fuzzy. In my case I decided to go with the DNS challenge route. Really the only reason I went with this [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>In this article we are gonna get into setting up Traefik to request dynamic certs from Lets Encrypt.  I had a few issues getting this up and running and the documentation is a little fuzzy.  In my case I decided to go with the DNS challenge route.  Really the only reason I went with this option is because I was having issues with the TLS and HTTP challenges.  Well as it turns out my issues didn&#8217;t have as much to do with my configuration as they did with my router.</p>



<p>Sometime in the past I had set up some special rules on my router to force all clients on my network to send DNS requests through a self hosted DNS server.  I did this to keep some of my &#8220;smart&#8221; devices from misbehaving by blocking there access to the outside world.  As it turns out some devices will ignore the DNS servers that you hand out via DHCP and will use their own instead.  That is of course unless you force DNS redirection but that is another post for another day.</p>



<p>Let&#8217;s revisit our current configuration:</p>



<pre class="wp-block-code"><code>version: '3'

services:
  reverse-proxy:
    # The official v2 Traefik docker image
    image: traefik:v2.11
    # Enables the web UI and tells Traefik to listen to docker
    command:
      - --api.insecure=true
      - --providers.docker=true
      - --providers.file.filename=/config.yml
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      # Set up LetsEncrypt
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
      - --certificatesresolvers.letsencrypt.acme.email=mikeconrad@onmail.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --entryPoints.web.http.redirections.entryPoint.to=websecure
      - --entryPoints.web.http.redirections.entryPoint.scheme=https
      - --entryPoints.web.http.redirections.entrypoint.permanent=true
      - --log=true
      - --log.level=INFO
#      - '--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory'

    environment:
      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
    ports:
      # The HTTP port
      - "80:80"
      - "443:443"
      # The Web UI (enabled by --api.insecure=true)
      - "8080:8080"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
      - ./volumes/traefik/logs:/logs
      - ./traefik/config.yml:/config.yml:ro
    networks:
      - traefik
  ots:
    image: luzifer/ots
    container_name: ots
    restart: always
    environment:
      # Optional, see "Customization" in README
      #CUSTOMIZE: '/etc/ots/customize.yaml'
      # See README for details
      REDIS_URL: redis://redis:6379/0
      # 168h = 1w
      SECRET_EXPIRY: "604800"
      # "mem" or "redis" (See README)
      STORAGE_TYPE: redis
    depends_on:
      - redis
    labels:
      - traefik.enable=true
      - traefik.http.routers.ots.rule=Host(`ots.hackanooga.com`)
      - traefik.http.routers.ots.entrypoints=websecure
      - traefik.http.routers.ots.tls=true
      - traefik.http.routers.ots.tls.certresolver=letsencrypt
    networks:
      - traefik
  redis:
    image: redis:alpine
    restart: always
    volumes:
      - ./redis-data:/data
    networks:
      - traefik
networks:
  traefik:
    external: true

</code></pre>



<p>Now that we have all of this in place there are a couple more things we need to do on the Cloudflare side:</p>



<h3 class="wp-block-heading">Step 1: Setup wildcard DNS entry</h3>



<p>This is pretty straightforward.  Follow the Cloudflare <a href="https://developers.cloudflare.com/dns/manage-dns-records/reference/wildcard-dns-records/" data-type="link" data-id="https://developers.cloudflare.com/dns/manage-dns-records/reference/wildcard-dns-records/">documentation</a> if you aren&#8217;t familiar with setting this up. </p>



<h3 class="wp-block-heading">Step 2: Create API Token</h3>



<p>This is where the Traefik documentation is a little lacking.  I had some issues getting this set up initially but ultimately found this <a href="https://go-acme.github.io/lego/dns/cloudflare/">documentation</a> which pointed me in the right direction.  In your Cloudflare account you will need to create an API token.  Navigate to the dashboard, go to your profile -> API Tokens and create new token.  It should have the following permissions:</p>



<pre class="wp-block-code"><code>Zone.Zone.Read
Zone.DNS.Edit</code></pre>



<figure class="wp-block-image size-full"><img decoding="async" width="1015" height="770" src="https://wordpress.hackanooga.com/wp-content/uploads/2024/03/image-9.png" alt="" class="wp-image-426" srcset="/wp-content/uploads/2024/03/image-9.png 1015w, /wp-content/uploads/2024/03/image-9-300x228.png 300w, /wp-content/uploads/2024/03/image-9-768x583.png 768w" sizes="(max-width: 1015px) 100vw, 1015px" /></figure>



<p>Also be sure to give it permission to access all zones in your account.  Now simply provide that token when starting up the stack and you should be good to go:</p>



<pre class="wp-block-code"><code>CF_DNS_API_TOKEN=&#91;redacted] docker compose up -d</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Traefik with Let&#8217;s Encrypt and Cloudflare (pt 1)</title>
		<link>/traefik-with-lets-encrypt-and-cloudflare-pt-1/</link>
		
		<dc:creator><![CDATA[mikeconrad]]></dc:creator>
		<pubDate>Thu, 01 Feb 2024 19:35:00 +0000</pubDate>
				<category><![CDATA[Cloudflare]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Self Hosted]]></category>
		<category><![CDATA[Traefik]]></category>
		<category><![CDATA[Blog Post]]></category>
		<guid isPermaLink="false">https://wordpress.hackanooga.com/?p=422</guid>

					<description><![CDATA[Recently I decided to rebuild one of my homelab servers. Previously I was using Nginx as my reverse proxy but I decided to switch to Traefik since I have been using it professionally for some time now. One of the reasons I like Traefik is that it is stupid simple to set up certificates and [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Recently I decided to rebuild one of my homelab servers.  Previously I was using Nginx as my reverse proxy but I decided to switch to Traefik since I have been using it professionally for some time now.  One of the reasons I like Traefik is that it is stupid simple to set up certificates and when I am using it with Docker I don&#8217;t have to worry about a bunch of configuration files.  If you aren&#8217;t familiar with how Traefik works with Docker, here is a brief example of a <code>docker-compose.yaml</code></p>



<pre class="wp-block-code"><code>version: '3'

services:
  reverse-proxy:
    # The official v2 Traefik docker image
    image: traefik:v2.11
    # Enables the web UI and tells Traefik to listen to docker
    command:
      - --api.insecure=true
      - --providers.docker=true
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      # Set up LetsEncrypt
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
      - --certificatesresolvers.letsencrypt.acme.email=user@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      # Redirect all http requests to https
      - --entryPoints.web.http.redirections.entryPoint.to=websecure
      - --entryPoints.web.http.redirections.entryPoint.scheme=https
      - --entryPoints.web.http.redirections.entrypoint.permanent=true
      - --log=true
      - --log.level=INFO
    # Needed to request certs via lets encrypt
    environment:
      - CF_DNS_API_TOKEN=&#91;redacted]
    ports:
      # The HTTP port
      - "80:80"
      - "443:443"
      # The Web UI (enabled by --api.insecure=true)
      - "8080:8080"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Used for storing letsencrypt certificates
      - ./letsencrypt:/letsencrypt
      - ./volumes/traefik/logs:/logs
    networks:
      - traefik
  ots:
    image: luzifer/ots
    container_name: ots
    restart: always
    environment:
      REDIS_URL: redis://redis:6379/0
      SECRET_EXPIRY: "604800"
      STORAGE_TYPE: redis
    depends_on:
      - redis
    labels:
      - traefik.enable=true
      - traefik.http.routers.ots.rule=Host(`ots.example.com`)
      - traefik.http.routers.ots.entrypoints=websecure
      - traefik.http.routers.ots.tls=true
      - traefik.http.routers.ots.tls.certresolver=letsencrypt
      - traefik.http.services.ots.loadbalancer.server.port=3000
    networks:
      - traefik
  redis:
    image: redis:alpine
    restart: always
    volumes:
      - ./redis-data:/data
    networks:
      - traefik
networks:
  traefik:
    external: true


</code></pre>



<p>In part one of this series I will be going over some of the basics of Traefik and how dynamic routing works.  If you want to skip to the good stuff and get everything configured with Cloudflare, you can skip to <a href="https://wordpress.hackanooga.com/traefik-with-lets-encrypt-and-cloudflare-pt-2/" data-type="post" data-id="425">part 2</a>.</p>



<p>This example set&#8217;s up the primary Traefik container which acts as the ingress controller as well as a  handy One Time Secret sharing <a href="https://github.com/Luzifer/ots">service</a> I use.  Traefik handles routing in Docker via labels.  For this to work properly the services that Traefik is trying to route to all need to be on the same Docker network.  For this example we created a network called traefik by running the following:</p>



<pre class="wp-block-code"><code>docker network create traefik
</code></pre>



<p>Let&#8217;s take a look at the labels we applied to the <code>ots</code> container a little closer:</p>



<pre class="wp-block-code"><code>    labels:
      - traefik.enable=true
      - traefik.http.routers.ots.rule=Host(`ots.example.com`)
      - traefik.http.routers.ots.entrypoints=websecure
      - traefik.http.routers.ots.tls=true
      - traefik.http.routers.ots.tls.certresolver=letsencrypt
      - traefik.http.services.ots.loadbalancer.server.port=3000</code></pre>



<p><code>traefik.enable=true</code> &#8211; This should be pretty self explanatory but it tells Traefik that we want it to know about this service.</p>



<p><code>traefik.http.routers.ots.rule=Host('ots.example.com') - This is where some of the magic comes in.</code>  Here we are defining a router called <code>ots</code>.  The name is arbitrary in that it doesn&#8217;t have to match the name of the service but for our example it does.  There are many rules that you can specify but the easiest for this example is host.  Basically we are saying that any request coming in for ots.example.com should be picked up by this router.  You can find more options for routers in the Traefik <a href="https://doc.traefik.io/traefik/routing/routers/">docs</a>.</p>



<p>&#8211; traefik.http.routers.ots.entrypoints=websecure<br>&#8211; traefik.http.routers.ots.tls=true<br>&#8211; traefik.http.routers.ots.tls.certresolver=letsencrypt<br><br>We are using these three labels to tell our router that we want it to use the websecure entrypoint, and that it should use the letsencrypt certresolver to grab it&#8217;s certificates.  websecure is an arbitrary name that we assigned to our :443 interface.  There are multiple ways to configure this, I choose to use the cli format in my traefik config:</p>



<p>&#8220;`</p>



<pre class="wp-block-code"><code>    command:
      - --api.insecure=true
      - --providers.docker=true
      # Our entrypoint names are arbitrary but these are convention.
      # The important part is the port binding that we associate.
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443

</code></pre>



<p>These last label is optional depending on your setup but it is important to understand as the documentation is a little fuzzy.</p>



<p>      &#8211; traefik.http.services.ots.loadbalancer.server.port=3000<br><br>Here&#8217;s how it works.  Suppose you have a container that exposes multiple ports.  Maybe one of those is a web ui and another is something that you don&#8217;t want exposed.  By default Traefik will try and guess which port to route requests to.  My understanding is that it will try and use the first exposed port.  However you can override this functionality by using the label above which will tell Traefik specifically which port you want to route to inside the container.<br><br>The service name is derived automatically from the definition in the docker compose file:</p>



<pre class="wp-block-code"><code><br>  ots: # This will become the service name<br>    image: luzifer/ots<br>    container_name: ots<br>    </code></pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
