Add support for cgi scripts
This commit is contained in:
parent
0e14141dde
commit
faa5fcbb08
4 changed files with 67 additions and 7 deletions
|
@ -19,7 +19,8 @@ RUN pip install pyyaml
|
|||
WORKDIR /app
|
||||
COPY ./gmnd/__init__.py .
|
||||
COPY ./content content
|
||||
COPY ./cgi-bin cgi-bin
|
||||
COPY config.yml .
|
||||
COPY --from=build-stage /app/certs certs
|
||||
EXPOSE 1965
|
||||
CMD ["python", "__init__.py", "--file", "config.yml" ]
|
||||
CMD ["python", "__init__.py", "--file", "./config.yml" ]
|
||||
|
|
10
README.md
10
README.md
|
@ -1,7 +1,7 @@
|
|||
# gMNd
|
||||
gMNd is my gemini server, which is written in python. Documentation will primarily be supplied via [gemini://mic.ke/gmnd/docs](gemini://mic.ke/gmnd/docs), but if you are not yet able to access content via gemini, here is a quick start guide for your viewing pleasure:
|
||||
gMNd is my gemini server, which is written in python. It has support for serving static files, or run cgi-scripts. Documentation will primarily be supplied via [gemini://mic.ke/gmnd/docs](gemini://mic.ke/gmnd/docs), but if you are not yet able to access content via gemini, here is a quick start guide for your viewing pleasure:
|
||||
|
||||
Currently it only serves static files. You can build and run it from the supplied Dockerfile if you so whish:
|
||||
You can build and run it from the supplied Dockerfile if you so whish:
|
||||
```
|
||||
docker build -t gmnd:latest .
|
||||
```
|
||||
|
@ -9,11 +9,11 @@ By just running it, it will create self signed certs and serve example content f
|
|||
```
|
||||
docker run -p 1965:1965 gmnd
|
||||
```
|
||||
A slightly more interesting thing it can do is serve your own content, in this example from /tmp/content on your host machine:
|
||||
A slightly more interesting thing it can do is serve your own content, in this example from /tmp/content on your host machine and cgi-scripts from /tmp/cgi-bin:
|
||||
```
|
||||
docker run --mount type=bind,source="/tmp/content,target=/app/content" -p 1965:1965 gmnd
|
||||
docker run --mount type=bind,source="/tmp/content,target=/app/content" --mount type=bind,source="/tmp/cgi-bin,target=/app/cgi-bin" -p 1965:1965 gmnd
|
||||
```
|
||||
Or even supply your own certificates from the outside, in this example in /usr/local/certs:
|
||||
Or even supply your own certificates from the outside, in this example in /usr/local/certs with static content from /tmp/content:
|
||||
```
|
||||
docker run --mount type=bind,source="/tmp/content,target=/app/content" --mount type=bind,source="/usr/local/certs,target=/app/certs" -p 1965:1965 gmnd
|
||||
```
|
||||
|
|
|
@ -2,3 +2,6 @@
|
|||
allow_dir_list: true
|
||||
listen_addr: '0.0.0.0'
|
||||
logg_level: 'DEBUG'
|
||||
#cgi_registry:
|
||||
# /.*:
|
||||
# - ./cgi-bin/envtest.sh
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import ssl
|
||||
import sys
|
||||
from socket import AF_INET, SHUT_RDWR, SO_REUSEADDR, SOCK_STREAM, SOL_SOCKET
|
||||
import subprocess
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import yaml
|
||||
|
@ -39,6 +41,8 @@ class gMNd:
|
|||
self.allow_dir_list = config_dict['allow_dir_list']
|
||||
if 'base_path' in config_dict:
|
||||
self.base_path = config_dict['base_path']
|
||||
if 'cgi_registry' in config_dict:
|
||||
self.cgi_registry = config_dict['cgi_registry']
|
||||
if 'listen_addr' in config_dict:
|
||||
self.listen_addr = config_dict['listen_addr']
|
||||
if 'listen_port' in config_dict:
|
||||
|
@ -67,6 +71,7 @@ class gMNd:
|
|||
newsocket, fromaddr = self.bindsocket.accept()
|
||||
logging.debug("Client connected: {}:{}".format(
|
||||
fromaddr[0], fromaddr[1]))
|
||||
|
||||
conn = ssl.wrap_socket(newsocket,
|
||||
server_side=True,
|
||||
certfile=self.server_cert,
|
||||
|
@ -86,7 +91,23 @@ class gMNd:
|
|||
|
||||
header = get_header()
|
||||
body = b""
|
||||
if os.path.isfile(self.base_path + path):
|
||||
|
||||
|
||||
try:
|
||||
for key, val in self.cgi_registry.items():
|
||||
if re.match(key, path):
|
||||
cgi = True
|
||||
env = self.get_env(url, fromaddr[0])
|
||||
script = val
|
||||
except:
|
||||
cgi = False
|
||||
script = None
|
||||
env = None
|
||||
|
||||
if cgi:
|
||||
body = run_cgi(script, env)[0]
|
||||
|
||||
elif os.path.isfile(self.base_path + path):
|
||||
if not path.endswith(".gmi"):
|
||||
header = get_header(
|
||||
'20',
|
||||
|
@ -133,6 +154,41 @@ class gMNd:
|
|||
contents = contents + b"=> " + entry.encode() + b"\r\n"
|
||||
return contents
|
||||
|
||||
def get_env(self, url, remote_addr):
|
||||
path = url.path.decode().rstrip()
|
||||
query = url.query.decode().rstrip()
|
||||
env = {}
|
||||
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
|
||||
env['GEMINI_DOCUMENT_ROOT'] = str(self.base_path)
|
||||
env['GEMINI_URL'] = str(url.geturl().decode().rstrip())
|
||||
env['GEMINI_URL_PATH'] = str(path)
|
||||
env['PATH_INFO'] = str(path)
|
||||
env['PATH_TRANSLATED'] = str(self.base_path + path)
|
||||
env['QUERY_STRING'] = str(query)
|
||||
env['REMOTE_ADDR'] = str(remote_addr)
|
||||
try:
|
||||
env['REMOTE_HOST'] = str(
|
||||
socket.getfqdn(socket.gethostbyaddr(remote_addr)[0]))
|
||||
except:
|
||||
env['REMOTE_HOST'] = str(remote_addr)
|
||||
env['SERVER_NAME'] = str(socket.getfqdn())
|
||||
env['SERVER_PORT'] = str(self.listen_port)
|
||||
env['SERVER_PROTOCOL'] = 'GEMINI'
|
||||
env['SERVER_SOFTWARE'] = 'gMNd'
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def run_cgi(script, menv):
|
||||
"""Run a command on system and capture result"""
|
||||
process = subprocess.Popen(script,
|
||||
env=menv,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
result = process.communicate()
|
||||
return result
|
||||
|
||||
|
||||
def get_header(status='20', meta=b"text/gemini"):
|
||||
metadict = {}
|
||||
|
|
Loading…
Add table
Reference in a new issue