Giter Club home page Giter Club logo

trusted-cgi's Introduction

Trusted-CGI

license donate

Lightweight self-hosted lambda/applications/cgi/serverless-functions engine.

see docs

Features:

  • No specific requirements: just one binary. Working "as-is"
  • One-click new lambda with public link and handler. Available immediately.
  • Rich API
  • Security: user switch, IP restrictions, Origin restrictions, tokens ....
  • Time limits
  • Permanent links (aliases)
  • Actions - independent instruction that could be run via UI/API on server
  • Scheduler: run actions in cron-tab like style
  • Queues and retries
  • ... etc - see docs

P.S

There is minimal version of trusted-cgi: nano-run. Check it out - it DevOps friendly with configuration-first approach (ie easier to use for infrastructure-as-a-code).

Installation

Since 0.3.3 Linux, Darwin and even Windows OS supported: pre-built binaries could be found in releases

TL;DR;

  • for production for debian servers - use apt.reddec.dev (see docs)
  • locally or non-debian server - download binary and run
  • for quick tests or for limited production - use docker image (docker run --rm -p 3434:3434 reddec/trusted-cgi)

See installation manual

Overview

The process flow is quite straightforward: one light daemon in background listens for requests and launches scripts/apps on demand. An executable shall read standard input (stdin) for request data and write a response to standard output (stdout).

Technically any script/application that can parse STDIN and write something to STDOUT should be capable of the execution.

Trusted-cgi designed keeping in mind that input and output data is quite small and contains structured data (json/xml), however, there are no restrictions on the platform itself.

Key differences with classic CGI:

  • Only request body is being piped to scripts input (CGI pipes everything, and application has to parse it by itself - it could be very not trivial and slow (it depends))
  • Request headers, form fields, and query params are pre-parsed by the platform and can be passed as an environment variable (see mapping)
  • Response headers are pre-defined in manifest

Due to changes, it's possible to make the simplest script with JSON input and output like this:

import sys
import json

request = json.load(sys.stdin) # read and parse request
response = ['hello', 'world']  # do some logic and make response
json.dump(response, sys.stdout)  # send it to client

Keep in mind, the platform also adds a growing number of new features - see features.

target audience

It's best (but not limited) for

  • for hobby projects
  • for experiments
  • for projects with a low number of requests: webhooks, scheduled processing, etc..
  • for a project working on low-end machines: raspberry pi, cheapest VPS, etc..

However, if your projects have overgrown the platform limitations, it should be quite easy to migrate to any other solutions, because most low-level details are hidden and could be replaced in a few days (basically - just wrap script to HTTP service)

Also, it is possible to scale the platform performance by just launching the same instances of the platform with a shared file system (or docker images) with a balancer in front of it.

Contributing

The platform is quite simple Golang project with Vue + Quasar frontend and should be easy for newcomers. Caveats and tips for backend check here

For UI check sub-repo

Any PR (docs, code, styles, features, ...) will be very helpful!

Please note, that Linux (including WSL2) or Darwin is mainly used for developing, therefore most helper scripts designed for those OS.

Requirements:

  • go 1.21 (actual version check in go.mod)
  • python3
  • pandoc
  • docker

For multiplatform build you may need bintools:

docker run --privileged --rm tonistiigi/binfmt --install all

trusted-cgi's People

Contributors

colonelpopcorn avatar joaopms avatar reddec avatar the-glu avatar willypillow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

trusted-cgi's Issues

Question: are cgi calls all queued?

I saw one example uses an Sqlite database but only one process at a time can write to them. Does trusted-cgi only run a single function at a time on the server?

Scheduler is starting all jobs at startup

Hello,

When the service is restarted, all scheduled jobs are started one, even when outside the specified schedule.

I'm not sure if it's a feature or not, but it would be great to at least have an option not to have this behavior :)

I tried to look inside the code but didn't find any obvious location, with some pointers I would be happy to write a PR if wanted.

Thanks,

Content types for various types of files in static

Hello,

It would be great to have dynamic content types for files in static. E.g. if a small CSS is needed for a project, it's doesn't work because (at least Firefox) refuse to use CSS that have the wrong content type.

Some basic rules (for css, js and images) should be enough, but more advanced options like per-extention header or automatic mime type detection would do the job :)

Thanks a lot and have a nice daym

archive/tar: Write too long on Windows

OS: Windows 10
Shell: PowerShell Core 7.1
Version: 0.3.6

On Windows, I am running into the following error message when attempting to download a newly-created lambda from my VPS:

PS> cgi-ctl download -l admin -p <mypassword> -u https://<mydomain>.dev -U 93470b46-f74a-4352-9c74-c7bbbfdd5eb9
2021/01/23 07:48:28 login...
2021/01/23 07:48:28 download...
download: -30000: archive/tar: write too long

Is there some combination of options that immediately strikes you as odd here, using SSL and being on Windows maybe?
According to some research this seems to be a common issue in Go, but I have 0 knowledge about the language. If you were to make a release with more verbose error handling then I could provide a more detailed error description :)

Token used before issued.

Hi! I stumbled upon this and excited to try it out, however I encountered this issue upon login:

Error: 403: token validation failed: Token used before issued

Downgrading to 0.3.6 seems to fix the issue.

How to run lambdas periodically?

Hi!

I would like to run some of my lambdas periodically and it seems there is no straightforward way to do that. What I did was I created a ping action, which curls lambda url and thus invokes it.

ping:
	curl https://my.function.url/

This is a hacky solution, but what is the intended way?

Is it possible to add launcher as an action into the scheduler?

Screenshot 2021-01-10 at 01 25 44

ping action on the screenshot is what I use right now and run is what I would like to have

Directories in templates

I am using latest docker image of trusted-cgi and was not able to create a lambda from Nim template with an error saying:

Error: -30000: create lambda: write file src/lambda.nim content: open /data/b37de1ee-1e86-4209-b817-7ecb321d696f/src/lambda.nim: no such file or directory

Nim template is the only one that has directory in the template definition - src/lambda.nim. So I then went through the sources and discovered, that files are created with WriteFile:

err := ioutil.WriteFile(destFile, []byte(content), 0755)

But WriteFile can't handle directories. It should be possible to parse file path and do something like MkdirAll with it. What do you think?

PS: I am not a Nim developer and have just discovered trusted-cgi a couple of days ago; you have done an amazing job, thank you for that simple and straightforward solution!

Allow to pre-provision lambdas

In some circumstances, initializing time for a lambda could be quite large. it would be great to be able to configure some lambdas with a configurable number of pre-provisionned processes.

Once a lambda is pre-provisionned, it is started unconditionally in a pool. The lambda can then initialize all it wants and once initialized will stop waiting on I/O (stdin and stdout). Upon invocation, one lambda from the pool would be used and the standard input will be written to and standard output will be read to, motioning the lambda forward. Once a lambda from the pool is taken, a new lambda would be started to refill the pool.

edit: This is incompatible with MethodEnv, PathEnv, InputHeaders and Query.

Implementation could be:

  • in lambda interface add Start() and Stop() functions that notifies when the lambda is made available and the lambda is destroyed.
  • Start() should probably be called in
    func (platform *platform) setupLambda(lambda application.Lambda) error {
  • Stop() should be probably called from
    func (platform *platform) Remove(uid string) {
  • Start() should run a specialized invoke function where stdin and stdout are pipes. The other end of pipes should be stored in a pool data structure as well as a context cancel function (for lambda timeout)
  • Invoke() should either take a running lambda from the pool and connect the pipes (io.Copy for that), ensure the cancel function is called at the timeout event, or if there is nothing on the pool, should invoke a new lambda instance
  • Stop() should cancel all running lambda instances in the pool
  • When updating the lambda manifest SetManifest() and reloadManifest() the pool should be adjusted

New home needed for debian package

Bintray is no longer available since JFrog shut it down in May of 2021. See this article here for more details. If and when another home is found for binary hosting the install guide will need to be updated to reflect this change.

This project is really cool, BTW!

replace 'sh' for 'bash' in docker container

In trying to avoid creating temporary files to store data which could just be kept in variables it is possible in bash to use the following syntax of process substitution and input redirection in order to pass data onto other commands:

{ read -d '' result ; }< <( curl http://myjsonapi.com/page1 | jq '.[0].somedatapoint' ) && anothercommand <<< "$result"

unfortunately this syntax isn't supported in 'sh' and an unexpected redirect error is received.
Is it possible to use bash in the docker containers instead.

[Bug?] "new folder" creates a new *file*

YyMS3SHS1N.mp4

See screencast ⬆️

I am a little confused as to how it's supposed to behave here, is this a bug or is the problem between keyboard and chair?

Enhancement - Detecting Remote IP from behind reverse proxy

If you have the trusted-cgi running behind a reverse proxy then the Remote IP address detected will be the internal IP address of the reverse proxy.

Is it possible to detect the client IP address using the Request Header Value e.g. X-Forwarded-For
This is usually the header that the proxies pass on to the backend server so they are aware of the original client IP address.

lambdas called by their alias are not shown in stats

Here a quick hack to fix that.

diff --git a/api/services/lambda_srv.go b/api/services/lambda_srv.go
index 1a4601c..8ffef74 100644
--- a/api/services/lambda_srv.go
+++ b/api/services/lambda_srv.go
@@ -132,7 +132,11 @@ func (srv *lambdaSrv) RenameFile(ctx context.Context, token *api.Token, uid stri
 }
 
 func (srv *lambdaSrv) Stats(ctx context.Context, token *api.Token, uid string, limit int) ([]stats.Record, error) {
-       return srv.tracker.LastByUID(uid, limit)
+       fn, err := srv.cases.Platform().FindByUID(uid)
+       if err != nil {
+               return make([]stats.Record, 0), err
+       }
+       return srv.tracker.LastByUID(uid, fn.Aliases, limit)
 }
 
 func (srv *lambdaSrv) Actions(ctx context.Context, token *api.Token, uid string) ([]string, error) {
diff --git a/stats/impl/memlog/dumped.go b/stats/impl/memlog/dumped.go
index 9ad84fa..557da81 100644
--- a/stats/impl/memlog/dumped.go
+++ b/stats/impl/memlog/dumped.go
@@ -2,6 +2,7 @@ package memlog
 
 import (
        "github.com/reddec/trusted-cgi/stats"
+       "github.com/reddec/trusted-cgi/types"
        "github.com/tinylib/msgp/msgp"
        "io/ioutil"
        "os"
@@ -105,8 +106,8 @@ func (d *dumped) Track(record stats.Record) {
        d.mem.Track(record)
 }
 
-func (d *dumped) LastByUID(uid string, limit int) ([]stats.Record, error) {
-       return d.mem.LastByUID(uid, limit)
+func (d *dumped) LastByUID(uid string, aliases types.JsonStringSet, limit int) ([]stats.Record, error) {
+       return d.mem.LastByUID(uid, aliases, limit)
 }
 
 func (d *dumped) Last(limit int) ([]stats.Record, error) {
diff --git a/stats/impl/memlog/nop.go b/stats/impl/memlog/nop.go
index 211e7a6..3532695 100644
--- a/stats/impl/memlog/nop.go
+++ b/stats/impl/memlog/nop.go
@@ -2,6 +2,7 @@ package memlog
 
 import (
        "github.com/reddec/trusted-cgi/stats"
+       "github.com/reddec/trusted-cgi/types"
 )
 
 func New(depth uint) *statLogger {
@@ -16,7 +17,7 @@ func (s *statLogger) Track(record stats.Record) {
        s.buffer.Add(record)
 }
 
-func (s *statLogger) LastByUID(uid string, limit int) ([]stats.Record, error) {
+func (s *statLogger) LastByUID(uid string, aliases types.JsonStringSet, limit int) ([]stats.Record, error) {
        if limit < 0 {
                return []stats.Record{}, nil
        } else if n := s.buffer.Len(); limit > n {
@@ -27,6 +28,8 @@ func (s *statLogger) LastByUID(uid string, limit int) ([]stats.Record, error) {
        for i := len(clone) - 1; i >= 0 && len(ans) < limit; i-- {
                if clone[i].UID == uid {
                        ans = append(ans, clone[i])
+               } else if _, ok := aliases[clone[i].UID]; ok {
+                       ans = append(ans, clone[i])
                }
        }
        return ans, nil
diff --git a/stats/interface.go b/stats/interface.go
index 92de645..7458bf3 100644
--- a/stats/interface.go
+++ b/stats/interface.go
@@ -25,7 +25,7 @@ type Recorder interface {
 // Reader from tracking systems. All returned records should be sorted from newest to oldest (by insertion moment)
 type Reader interface {
        // Last records for specific app with limits
-       LastByUID(uid string, limit int) ([]Record, error)
+       LastByUID(uid string, aliases types.JsonStringSet, limit int) ([]Record, error)
        // Last all records
        Last(limit int) ([]Record, error)
 }

just wanted to say

This is awesome, thank you for this.
I tried running a python function and it seems quite fast too!

In the last years I've been observing & rethinking exactly the same phenomenon as this project seems to adress: offering a alternative to container bloat & complexity.
Sometimes scaling jobs horizontally is fine without kubernetes but using ssh (https://github.com/coderofsalvation/clussh e.g.) or Nats.io.
I realized how 'trusted' CGI would save so much development/maintenance time.
I think the adoption of this project will grow inherent to the people realizing the container-dungeons they find themselves in.

Tips:

Perhaps the description of this repo could include these (albeit cringy) words: serverless, FaaS, PaaS, baremetal, lowcode, polyglot e.g.

(That way I would have found it earlier I think. I really think this project is heavily underrated, I might do a video on it soon)

Another suggestion: you could mention in the docs/UI using https://github.com/containers/bubblewrap for untrusted processes (it allows sandboxing processes on a real lowlevel, and is a quite popular solution to the notorious slow startup time of kubernetes/docker ephemeral containers).
Ultimately shipping templates with bubblewrap, nice, or ionice would be epic.
Where can I submit templates?

Sidenote: I'm also curious how it could play with fellow go-binaries like nats.io, websocketd and pocketbase.

Anyways, just my few cents, you can consider this issue done btw.

Split CLI Tool and Service debian packages

First off: awesome project, much less overhead than all the other solutions I tried.

It would be great if the debian packages for the service (trusted-cgi) and the cli tool would be seperate. So you can install it on a server and use it remotely without installing the service, too.

Have a great day!

Link in README does not work (installation)

I just wanted to read the installation instructions in the README-File.

I wanted to click on the following content:
See [installation manual](https://trusted-cgi.reddec.net/installation)

Unfortunately the link did not work. Where can I find the detailed installation instructions?
Thanks for updating.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.