[HN Gopher] Pure Bash Web Server
       ___________________________________________________________________
        
       Pure Bash Web Server
        
       Author : shakna
       Score  : 92 points
       Date   : 2024-02-14 13:55 UTC (2 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | VWWHFSfQ wrote:
       | A long time ago I made a similarly pure bash version of something
       | like tcpdump just parsing various packets and protocols off a raw
       | socket. I wish I still had that code somewhere. It was pretty
       | much the slowest and least-robust thing of all time but was kind
       | of fun to play around with.
       | 
       | cool project
        
       | h4sh wrote:
       | theres literally c files in that repo how is that pure bash
        
         | daef wrote:
         | did you even bother reading the readme?
        
           | simultsop wrote:
           | Shouldn't a program's name be sufficient?
        
             | rovr138 wrote:
             | They're digging through code. The least they could do is
             | read the readme.
        
             | tekla wrote:
             | In the same way that Hacker News is not news for hackers,
             | no.
        
               | bhaney wrote:
               | It's not?
        
         | scoopertrooper wrote:
         | The project patches accept to handle multiple requests.
         | However, the project works without the patch being applied, so
         | it's fair to say that this is a pure bash web server.
        
         | 000ooo000 wrote:
         | Yep, there's a .csv file as well so clearly Microsoft Excel is
         | in play too. /s
        
           | AdamJacobMuller wrote:
           | and it's not even a CSV, not a comma in sight!
           | 
           | I feel lied to and I want a refund.
        
         | skywhopper wrote:
         | The C file is a patch to Bash to fix a bug in an existing Bash
         | builtin that will be included in the next Bash release.
        
       | cduzz wrote:
       | I suspect lots of people have written a "use tcpserver or inetd
       | and feed stdout to a shell script" antics.
       | 
       | The thing is, shell can't cope with nulls -- if you do something
       | like                 n=$(gzip -9 < /etc/passwd)       gzip -9 <
       | /etc/passwd | sum       echo "$n" | sum
       | 
       | This falls apart because shell just can't deal with nulls.
       | 
       | You can probably hack around all those issues, and may not run
       | into this too much, at first, in a web server, but golly you'll
       | pretty quickly fall into a pit.
        
         | mike_d wrote:
         | tr -dc '[[:print:]]'
         | 
         | Will sanitize strings of non-printable characters. While it is
         | true that you can't have nulls inside bash variables, your
         | example actually contains the correct syntax if you just remove
         | the first and last lines.
        
           | cduzz wrote:
           | well, sure, you can use some external program do process the
           | stdout; then it's no longer "pure bash" which is fine, nobody
           | grades ingots of script based on if they're 90% or 99% or 70%
           | "pure" shell.
           | 
           | But -- importantly -- running                 n=$(gzip -9 <
           | /etc/passwd | tr -dc '[[:print:]]')
           | 
           | may process the nulls, but is it reversible? Can I now send
           | $n into gzip -d and get whatever I put into it out?
           | 
           | I can do things that are reversible --
           | n=$(gzip -9 < /etc/passwd | base64 )
           | 
           | But now I can't process the output "natively" except by
           | calling base64 every time.
           | 
           | And maybe I've gotten myself into this hole because sometimes
           | the contents of $n have nulls and other times not?
           | 
           | Pure shell is a road to madness. Don't ask me how I know...
        
             | mike_d wrote:
             | You are in this mess because you are supposed to be piping
             | binary data or using temp files, not putting it in
             | variables. Also bash scripts are just glue between external
             | programs.
             | 
             | I mentioned the sanitization in the context of taking user
             | input (since we are talking about a bash web server)
             | because I thought you were pointing out a user could do bad
             | things by feeding in nulls.
        
               | cduzz wrote:
               | I'm just pointing out the insanity / inanity of "pure"
               | shell anything. There are lots of other gotchas hiding in
               | shell that you wouldn't encounter in other languages.
               | 
               | As glue, shell's wonderful. Reading from /dev/tcp/ and
               | such is a cute trick but ultimately a dead dead dead end.
        
           | throwway120385 wrote:
           | Yeah you can pipe nulls between processes just fine. You just
           | can't print them in a shell.
        
             | cduzz wrote:
             | In the case of                 gzip -9 < /dev/random | dd
             | of=/tmp/gibberish
             | 
             | the shell's not actually doing anything but forking things
             | and connecting file descriptors of processes to each other
             | gzip -9 < /dev/random | while read line ; do echo "$line" ;
             | done > /tmp/gibberish
             | 
             | The stdout of gzip _is_ being processed by shell, and will
             | make all the nulls go away.
             | 
             | (edited to add another example:)
             | 
             | Similarly - it isn't _printing_ that you can 't do -- it's
             | _anything_ -- consider:                 case $(cat /bin/sh)
             | in         $(cat /bin/bash)) echo "they're the same!" ;;
             | *) echo "they're not the same!" ;;       esac
             | 
             | This is obviously an insane way to see if two files are
             | identical, but worse -- it's going to fail for two
             | different files whose only difference is how many nulls are
             | in the file.
        
           | 082349872349872 wrote:
           | or use vis(1) on both sides of bash-land
        
       | solatic wrote:
       | Projects like this reiterate just how important it is, from a
       | security perspective, to ensure your production services are
       | running in containers without an included shell. If an attacker
       | can get a shell, they can do pretty much anything.
       | 
       | Debug containers are now a stable feature in Kubernetes. It
       | honestly boggles my mind how companies will throw so much time,
       | money, and effort into the cybersecurity product du jour when
       | they can get the vast majority of the value by moving everything
       | into distroless, shell-less containers running on managed VMs
       | that are optimized for container workloads.
        
         | robinhoodexe wrote:
         | Out of curiosity, how would you run Python or R workloads in
         | kubernetes without a distro or shell?
        
           | jonhohle wrote:
           | Python needs an ld.so and libc (minimally) but not a shell or
           | other external utilities. Shebang scripts are loaded by
           | ld.so, not the shell.
        
             | khrbtxyz wrote:
             | Shebang scripts are supported directly by the kernel via
             | the exec family of system calls, so ld.so shouldn't be
             | involved.
             | 
             | https://github.com/torvalds/linux/blob/master/fs/Kconfig.bi
             | n...                 config BINFMT_SCRIPT      tristate
             | "Kernel support for scripts starting with #!"      default
             | y      help        Say Y here if you want to execute
             | interpreted scripts starting with        #! followed by the
             | path to an interpreter.
        
             | thecodedmessage wrote:
             | Yeah, I remember reading the code in the kernel that
             | handles shebang a long time ago. ld.so is not involved.
        
             | usr1106 wrote:
             | Python with batteries included, doesn't that mean exploit
             | tools included?
             | 
             | No personal attack intended, I am wondering this about my
             | own embedded product which contains Python.
        
           | ttymck wrote:
           | https://stackoverflow.com/questions/62581924/is-there-a-
           | way-...
        
           | oneshtein wrote:
           | Python is better than shell, so intruder will use it first.
        
         | riddley wrote:
         | Please do a write-up of these debug features. I'd love to learn
         | about them.
        
           | deathanatos wrote:
           | Have you read the docs?
           | https://kubernetes.io/docs/tasks/debug/debug-
           | application/deb...
        
         | swozey wrote:
         | I work on multi-tenant k8s clusters at CDNs and used to work at
         | Rancher and have seen just about every multi-tenant / federated
         | deployment there is, nasa, meta, etc. Stuff where even the
         | hardware vfio paths mounting the nic or gpu channels keep users
         | apart from one another, and the entire path out of the cluster
         | are completely apart from k8s and any userspace and would be
         | something like multus as a shim- what exactly are you referring
         | to that can cgroup hop via a shell that we're not currently
         | mitigating? It's the hardware being infected by something we
         | worry about at this level.
         | 
         | A lot of the CDNs even use tools like kubevirt where the
         | segmentation is even further. And then we have gvisor,
         | firecracker, etc.
         | 
         | I admittedly haven't touched k8s code since 1.18 but I can't
         | think of anything like you're referring to and I definitely
         | would like to know about it.
         | 
         | Thanks.
        
         | kiririn wrote:
         | Also can go even simpler and use apparmor and/or systemd
         | hardening instead of containers
        
         | deathanatos wrote:
         | Debug containers have been a bit of a let down in UX. It's
         | rather difficult to do sort of basic stuff, and a lot of stuff
         | is hidden behind flags.
         | 
         | E.g., if you just sort of roll with the defaults, you're
         | dropped into a pod in a very confused state: `ps -ef` says
         | nothing in your debugee is running, and the filesystem of the
         | debugee is nowhere to be found.
         | 
         | You can work around both of those (the first is --target, but
         | the latter requires an intricate SO answer1) but its the sort
         | of thing that would be nicer out of the box?
         | 
         | The node debugging mode is a bit better: by default, puts you
         | in at least the host pidns, and mounts the host FS.
         | 
         | 1https://stackoverflow.com/questions/73355970/how-to-get-
         | acce...
        
           | bravetraveler wrote:
           | As someone who doesn't do much (any) k8s...
           | 
           | Seems only marginally better than using nsenter on a
           | privileged container to just go muck with the host
        
           | freedomben wrote:
           | I've also been disappointed with debug containers. They are
           | often not useful for debugging trick production-only issues
           | because so many of those issues are related to container
           | state, which can be (often is) different inside the
           | container. Certain languages/platforms and developer
           | discipline are better about this than others, like if you're
           | using functional/immutable languages then it's less of an
           | issue.
           | 
           | For applications that aren't super high security, I've been
           | really appreciating using immutable hosts (that get regularly
           | updated/rotated), along with CI/CD that is constantly
           | rebuilding from source, applying latest software updates, and
           | deploying the latest version of the app. Combined with other
           | tools like scanners, and de-bloating your images, it really
           | raises the height of the fruit.
        
       | gtroja wrote:
       | The conectiva Linux distro had a programmer that wrote a book on
       | Shell Script in which he implemented a bash server, but as apache
       | cgi scripts. I learned to properly program with that book
       | 
       | https://www.amazon.com.br/Script-Profissional-Aurelio-Marinh...
        
       | dzove855 wrote:
       | Creator here:
       | 
       | I'm really happy to see somebody shared it on hackernews :D If
       | you have some questions, feel free to ask me
        
         | zamadatix wrote:
         | Hey, great work! Reading through I started wonder how necessary
         | the loadables are? It'd be fun to have one that's not dependent
         | on loadbales, even if it's not as clean. E.g. could mktemp be
         | replaced with a timestamp named directory or something? Can rm
         | be avoided by just allowing garbage to pile up? Is finfo
         | something that can be worked around in some way?
        
           | dzove855 wrote:
           | Hello,
           | 
           | You could avoid loadables.
           | 
           | Finfo <- load file inside a variable and get the size Mktemp
           | <- like you said with timestamp Rm <- with a fifo or variable
        
             | zamadatix wrote:
             | Ah "stick it in a variable" seems obvious now, good point!
        
       | QuadmasterXLII wrote:
       | For your consideration, the jukebox from my old CS club:
       | #!/bin/bash         cd songs         if [[ ! -p q ]]; then
       | mkfifo q         fi              (while true; do echo "playing" |
       | nc -l 10000 | head -n 1 | cut -f 2 -d " " | cut -b 2- | grep -E
       | "^[a-zA-Z0-9/:.?=-]{1,}$" > q; done) &              while true;
       | do             if read a <q; then             echo $a;
       | fn=$(youtube-dl -f m4a --id --get-filename $a);
       | youtube-dl -f m4a --id $a             echo $fn
       | mplayer $fn             fi         done
       | 
       | To queue up a song, we'd find it on youtube and prepend
       | "http://jukebox.local:10000/" to the url
        
         | zamadatix wrote:
         | More nc, cut, grep, head, mkfifo, youtube-dl, and mplayer than
         | anything about Bash.
        
           | NegativeLatency wrote:
           | If bash is passing and handling input from an http request
           | that's pretty much good enough for me.
        
             | zamadatix wrote:
             | It's not really doing that though. Nc+head/cut/grep are and
             | youtube-dl is grabbing the data. Bash in this case is just
             | orchestrating the order of communication between the tools
             | doing the actual work, as a normal Bash script.
             | 
             | The post is about doing all of this in pure bash builtins
             | like /dev/tcp and bash functions. Not about gluing together
             | tools which do the work.
        
               | gtroja wrote:
               | Nonetheless pretty wicked app made with bash
        
               | zamadatix wrote:
               | Yeah, it's a nifty little tool. On the other hand when a
               | really interesting post on pure bash exposition comes up
               | the comments section turns to talking about plain bash
               | scripts they've made or know of instead. There are
               | several normal scripts replied in this post and a few
               | others via links, more than actual conversation around
               | what the post is actually about (so far). That's all I
               | was commenting on, not the value of the tool. It's a hill
               | :).
        
               | QuadmasterXLII wrote:
               | your hill is valid, sorry
        
             | remram wrote:
             | That's not the rules used in this submission though.
             | 
             | > A purely bash web server, no socat, netcat, etc...
        
           | agumonkey wrote:
           | as always
        
       | recursivedoubts wrote:
       | see also https://bashsta.cc
        
       | calvinmorrison wrote:
       | Of course 9front ships a rc based http server
       | 
       | https://werc.cat-v.org/docs/web-server-setup/rc-httpd
        
         | cf100clunk wrote:
         | Related to this?
         | 
         | https://news.ycombinator.com/item?id=7614718
        
       | goombacloud wrote:
       | When socat is around a simple server can also be constructed with
       | it:                       tee /tmp/server > /dev/null <<'EOF'
       | #!/bin/bash             set -euo pipefail             SERVE="$1"
       | TYPE="$2"             read -a WORDS             if [
       | "${#WORDS[@]}" != 3 ] || [ "${WORDS[0]}" != "GET" ]; then
       | echo -ne "HTTP/1.1 400 Bad request\r\n\r\n"; exit 0
       | fi             # Subfolders are not supported for security
       | reasons as this avoids having to deal with ../../ attacks
       | FILE="${SERVE}/$(basename -- "${WORDS[1]}")"             if [ -d
       | "${FILE}" ] || [ ! -e "${FILE}" ]; then               echo -ne
       | "HTTP/1.1 404 Not found\r\n\r\n" ; exit 0             fi
       | echo -ne "HTTP/1.1 200 OK\r\n"             echo -ne "Content-
       | Type: ${TYPE};\r\n"             LEN=$(stat -L --printf='%s\n'
       | "${FILE}")             echo -ne "Content-Length: ${LEN}\r\n"
       | echo -ne "\r\n"             cat "${FILE}"             EOF
       | chmod +x /tmp/server             # switch from "text/plain" to
       | "application/octet-stream" for file downloads             socat
       | TCP-LISTEN:8000,reuseaddr,fork SYSTEM:'/tmp/server /tmp/ text-
       | plain'
       | 
       | # test: curl -v http://localhost:8000/server
        
         | cf100clunk wrote:
         | There are other such tiny web server tricks out there too, but
         | his GitHub README says:                 A purely bash web
         | server, no socat, netcat, etc...
        
       | dang wrote:
       | Related:
       | 
       |  _Show HN: A pure bash web server. No netcat, socat, etc._ -
       | https://news.ycombinator.com/item?id=29794979 - Jan 2022 (97
       | comments)
        
       ___________________________________________________________________
       (page generated 2024-02-16 23:00 UTC)