Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Please login to access the resource
home:ph03nix:tools
weblug
weblug-0.3.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File weblug-0.3.obscpio of Package weblug
07070100000000000081A4000000000000000000000001644A5E8E000001F1000000000000000000000000000000000000001600000000weblug-0.3/.gitignore# ---> Go # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore # # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib /weblug # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ # Go workspace file go.work 07070100000001000081A4000000000000000000000001644A5E8E00000436000000000000000000000000000000000000001300000000weblug-0.3/LICENSEMIT License Copyright (c) <year> <copyright holders> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 07070100000002000081A4000000000000000000000001644A5E8E000000CF000000000000000000000000000000000000001400000000weblug-0.3/Makefiledefault: all all: weblug weblug: cmd/weblug/*.go go build -o weblug $^ static: cmd/weblug/*.go CGO_ENABLED=0 go build -ldflags="-w -s" -o weblug $^ test: weblug sudo bash -c "cd test && ./blackbox.sh" 07070100000003000081A4000000000000000000000001644A5E8E000004E7000000000000000000000000000000000000001500000000weblug-0.3/README.md# weblug Simple webhook receiver program. `weblug` is is a configurable webhook receiver that allows users to define custom programs and script to be executed when a webhook is triggered. The configuration happens via a simple [yaml file](weblug.yml). `weblug` supports multiple webhooks, limitations for concurrent web hooks to be executed, background execution and running webhooks as separate user (`uid`/`gid`). And all of this in a tidy, easy-to-use yaml file! ## Usage To use `weblug` you need to define webooks in a yaml file. See [weblug.yml](weblug.yml) for an example configuration. Then run ./weblug YAML-FILE This starts the webserver and listens for incoming webhooks. `weblug` can run as any user, however for custom `uid`/`gid` webhooks, the program needs to run as root. ## Build make # Build weblug make static # Make a static binary ## Run as systemd unit This repository provides an example [weblug.service](weblug.service), which can be used to start `weblug` as systemd service. This file can be placed in `/etc/systemd/system/weblug.service` and in conjunction with an adequate `weblug.yml` file e.g. in `/etc/weblug.yml` this provides a way of running weblug as a native systemd service.07070100000004000041ED000000000000000000000001644A5E8E00000000000000000000000000000000000000000000000F00000000weblug-0.3/cmd07070100000005000041ED000000000000000000000001644A5E8E00000000000000000000000000000000000000000000001600000000weblug-0.3/cmd/weblug07070100000006000081A4000000000000000000000001644A5E8E0000043C000000000000000000000000000000000000002000000000weblug-0.3/cmd/weblug/config.gopackage main import ( "fmt" "io/ioutil" "gopkg.in/yaml.v2" ) type Config struct { Settings ConfigSettings `yaml:"settings"` Hooks []Hook `yaml:"hooks"` } type ConfigSettings struct { BindAddress string `yaml:"bind"` // Bind address for the webserver } func (cf *Config) SetDefaults() { cf.Settings.BindAddress = ":2088" } // Check performs sanity checks on the config func (cf *Config) Check() error { if cf.Settings.BindAddress == "" { return fmt.Errorf("no bind address configured") } for _, hook := range cf.Hooks { if hook.Name == "" { return fmt.Errorf("hook without name") } if hook.Route == "" { return fmt.Errorf("hook %s with no route", hook.Name) } if hook.Command == "" { return fmt.Errorf("hook %s with no command", hook.Name) } if hook.Concurrency < 1 { hook.Concurrency = 1 } } return nil } func (cf *Config) LoadYAML(filename string) error { content, err := ioutil.ReadFile(filename) if err != nil { return err } if err := yaml.Unmarshal(content, cf); err != nil { return err } return cf.Check() } 07070100000007000081A4000000000000000000000001644A5E8E00001498000000000000000000000000000000000000001E00000000weblug-0.3/cmd/weblug/hook.gopackage main import ( "fmt" "net" "os/exec" "strings" "sync/atomic" "syscall" ) type Hook struct { Name string `yaml:"name"` // name of the hook Route string `yaml:"route"` // http route Command string `yaml:"command"` // Actual command to execute Background bool `yaml:"background"` // Run in background Concurrency int `yaml:"concurrency"` // Number of allowed concurrent runs concurrentRuns int32 // Number of current concurrent runs UID int `yaml:"uid"` // UID to use when running the command GID int `yaml:"gid"` // GID to use when running the command Output bool `yaml:"output"` // Print program output AllowAddresses []string `yaml:"allowed"` // Addresses that are explicitly allowed BlockedAddresses []string `yaml:"blocked"` // Addresses that are explicitly blocked HttpBasicAuth BasicAuth `yaml:"basic_auth"` // Optional requires http basic auth } type BasicAuth struct { Username string `yaml:"username"` // Optional: Required username for the webhook to be allowed. If empty, any username will be accepted Password string `yaml:"password"` // If set, the http basic auth is enabled and the request must contain this password for being allowed } // Tries to lock a spot. Returns false, if the max. number of concurrent runs has been reached func (hook *Hook) TryLock() bool { res := int(atomic.AddInt32(&hook.concurrentRuns, 1)) if res > hook.Concurrency { atomic.AddInt32(&hook.concurrentRuns, -1) return false } return true } func (hook *Hook) Unlock() { atomic.AddInt32(&hook.concurrentRuns, -1) } // Split a command into program arguments, obey quotation mark escapes func cmdSplit(command string) []string { null := rune(0) esc := null // Escape character or \0 if not escaped currently ret := make([]string, 0) buf := "" // Current command for _, char := range command { if esc != null { if char == esc { esc = null } else { buf += string(char) } } else { // Check for quotation marks if char == '\'' || char == '"' { esc = char } else if char == ' ' { ret = append(ret, buf) buf = "" } else { buf += string(char) } } } // Remaining characters if buf != "" { ret = append(ret, buf) buf = "" } return ret } // Run executes the given command and return it's return code. It also respects the given concurrency number and will block until resources are free func (hook *Hook) Run() error { split := cmdSplit(hook.Command) args := make([]string, 0) if len(split) > 1 { args = split[1:] } cmd := exec.Command(split[0], args...) if hook.UID > 0 || hook.GID > 0 { cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(hook.UID), Gid: uint32(hook.GID)} } if hook.Output { buf, ret := cmd.Output() fmt.Println(string(buf)) return ret } return cmd.Run() } func isAddressInList(addr string, addrList []string) (bool, error) { ip, _, err := net.ParseCIDR(addr) if err != nil { return false, err } for _, item := range addrList { iAddr, iNet, err := net.ParseCIDR(item) if err != nil { return false, err } if ip.Equal(iAddr) { return true, nil } if iNet.Contains(ip) { return true, nil } } return false, nil } // Extract only the CIDR address from the given address identifier. This removes the port, if present func cidr(addr string) string { // Check for IPv6 address s, e := strings.Index(addr, "["), strings.Index(addr, "]") if s >= 0 && e > 0 { return addr[s+1:e] + "/128" } // Simply remove the port i := strings.Index(addr, ":") if i > 0 { return addr[:i-1] + "/32" } return addr + "/32" } // IsAddressAllowed checks if the hook allows the given address. An address is allowed, if it is present in the AllowAddresses list (if non-empty) and if it is not present in the BlockedAddresses list (if non-empty) func (hook *Hook) IsAddressAllowed(addr string) (bool, error) { if addr == "" { // If we cannot determine the source address, but there are element in either the Allow or the Block list, the only safe thing we can do is to reject if hook.AllowAddresses != nil && len(hook.AllowAddresses) > 0 { return false, fmt.Errorf("no source address") } if hook.BlockedAddresses != nil && len(hook.BlockedAddresses) > 0 { return false, fmt.Errorf("no source address") } } addr = cidr(addr) if hook.AllowAddresses != nil && len(hook.AllowAddresses) > 0 { // If AllowAddresses is defined and not empty, the given addr must be in the AllowAddresses list found, err := isAddressInList(addr, hook.AllowAddresses) if err != nil { return false, err } // If not present in the list, block the request. Otherwise we still need to pass the BlockedAddresses check if !found { return false, err } } if hook.BlockedAddresses != nil && len(hook.BlockedAddresses) > 0 { // If BlockedAddresses is defined and not empty, the given addr must not be in the BlockedAddresses list found, err := isAddressInList(addr, hook.BlockedAddresses) if err != nil { return false, err } if found { return false, err } } return true, nil } 07070100000008000081A4000000000000000000000001644A5E8E000016F3000000000000000000000000000000000000002000000000weblug-0.3/cmd/weblug/weblug.go/* * weblug main program */ package main import ( "fmt" "log" "net/http" "os" "os/signal" "syscall" ) var cf Config type Handler func(http.ResponseWriter, *http.Request) func usage() { fmt.Println("weblug is a simple webhook receiver") fmt.Printf("Usage: %s [OPTIONS] YAML1[,YAML2...]\n\n", os.Args[0]) fmt.Println("OPTIONS") fmt.Println(" -h, --help Print this help message") fmt.Println() fmt.Println("The program loads the given yaml files for webhook definitions") } // awaits SIGINT or SIGTERM func awaitTerminationSignal() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-sigs fmt.Println(sig) os.Exit(1) }() } // Perform sanity check on hooks func sanityCheckHooks(hooks []Hook) error { uid := os.Getuid() for _, hook := range hooks { // If a hook sets a custom uid or gid, ensure we're running as root, otherwise print a warning if hook.UID != 0 && uid != 0 { fmt.Fprintf(os.Stderr, "Warning: Hook '%s' sets 'uid = %d' but we're not running as root\n", hook.Name, hook.UID) } if hook.GID != 0 && uid != 0 { fmt.Fprintf(os.Stderr, "Warning: Hook '%s' sets 'gid = %d' but we're not running as root\n", hook.Name, hook.GID) } } return nil } func main() { cf.SetDefaults() if len(os.Args) < 2 { usage() } for _, arg := range os.Args[1:] { if arg == "" { continue } else if arg == "-h" || arg == "--help" { usage() os.Exit(0) } else { if err := cf.LoadYAML((arg)); err != nil { fmt.Fprintf(os.Stderr, "yaml error: %s\n", err) os.Exit(1) } } } if len(cf.Hooks) == 0 { fmt.Fprintf(os.Stderr, "error: no webhooks defined\n") os.Exit(2) } // Sanity check if err := sanityCheckHooks(cf.Hooks); err != nil { fmt.Fprintf(os.Stderr, "hook sanity check failed: %s\n", err) os.Exit(3) } // Create default handlers http.HandleFunc("/health", createHealthHandler()) http.HandleFunc("/health.json", createHealthHandler()) http.HandleFunc("/index", createDefaultHandler()) http.HandleFunc("/index.htm", createDefaultHandler()) http.HandleFunc("/index.html", createDefaultHandler()) http.HandleFunc("/robots.txt", createRobotsHandler()) // Register hooks for i, hook := range cf.Hooks { if hook.Route == "" { fmt.Fprintf(os.Stderr, "Invalid hook %s: No route defined\n", hook.Name) } if hook.Concurrency < 1 { hook.Concurrency = 1 } log.Printf("Webhook %d: '%s' [%s] \"%s\"\n", i, hook.Name, hook.Route, hook.Command) http.HandleFunc(hook.Route, createHandler(hook)) } awaitTerminationSignal() log.Printf("Launching webserver on %s", cf.Settings.BindAddress) log.Fatal(http.ListenAndServe(cf.Settings.BindAddress, nil)) } // create a http handler function from the given hook func createHandler(hook Hook) Handler { return func(w http.ResponseWriter, r *http.Request) { log.Printf("GET %s %s", r.RemoteAddr, hook.Name) // Check if adresses are allowed or blocked allowed, err := hook.IsAddressAllowed(r.RemoteAddr) if err != nil { log.Printf("ERR: Error checking for address permissions for hook \"%s\": %s", hook.Name, err) w.WriteHeader(500) fmt.Fprintf(w, "{\"status\":\"fail\",\"reason\":\"server error\"}") return } if !allowed { log.Printf("Blocked: '%s' for not allowed remote end %s", hook.Name, r.RemoteAddr) // Return a 403 - Forbidden w.WriteHeader(403) fmt.Fprintf(w, "{\"status\":\"blocked\",\"reason\":\"address not allowed\"}") return } // Check for basic auth, if enabled if hook.HttpBasicAuth.Password != "" { username, password, ok := r.BasicAuth() if !ok { // Return a 403 - Forbidden w.WriteHeader(401) fmt.Fprintf(w, "{\"status\":\"unauthorized\",\"message\":\"webhook requires authentication\"}") return } if hook.HttpBasicAuth.Password != password || (hook.HttpBasicAuth.Username != "" && hook.HttpBasicAuth.Username != username) { // Return a 403 - Forbidden w.WriteHeader(403) fmt.Fprintf(w, "{\"status\":\"blocked\",\"reason\":\"not authenticated\"}") return } } // Check for available slots if !hook.TryLock() { log.Printf("ERR: \"%s\" max concurrency reached", hook.Name) // Return a 503 - Service Unavailable error w.Header().Add("Content-Type", "application/json") w.Header().Add("Retry-After", "120") // Suggest to retry after 2 minutes w.WriteHeader(503) fmt.Fprintf(w, "{\"status\":\"fail\",\"reason\":\"max concurrency reached\"}") return } if hook.Background { // Execute command in background w.Header().Add("Content-Type", "application/json") w.WriteHeader(200) fmt.Fprintf(w, "{\"status\":\"ok\"}") go func() { defer hook.Unlock() if err := hook.Run(); err != nil { log.Printf("Hook \"%s\" failed: %s", hook.Name, err) } else { log.Printf("Hook \"%s\" completed", hook.Name) } }() } else { defer hook.Unlock() if err := hook.Run(); err != nil { log.Printf("ERR: \"%s\" exec failure: %s", hook.Name, err) w.Header().Add("Content-Type", "application/json") w.WriteHeader(500) fmt.Fprintf(w, "{\"status\":\"fail\",\"reason\":\"program error\"}") } else { w.Header().Add("Content-Type", "application/json") w.WriteHeader(200) fmt.Fprintf(w, "{\"status\":\"ok\"}") } } } } func createHealthHandler() Handler { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(200) fmt.Fprintf(w, "{\"status\":\"ok\"}") } } func createDefaultHandler() Handler { return func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) fmt.Fprintf(w, "weblug - simple webhook handler") } } func createRobotsHandler() Handler { return func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) fmt.Fprintf(w, "User-agent: *\nDisallow: /") } } 07070100000009000041ED000000000000000000000001644A5E8E00000000000000000000000000000000000000000000000F00000000weblug-0.3/doc0707010000000A000081A4000000000000000000000001644A5E8E00000C9E000000000000000000000000000000000000001800000000weblug-0.3/doc/weblug.8." Manpage for weblug ." Contact felix@feldspaten.org to correct errors and/or typos. .TH weblug 8 "27 Apr 2023" "1.0" "weblug man page" .SH NAME weblug - Simple webhook receiver program .SH SYNOPSIS weblug [OPTIONS] YAML1[,YAML2...] .SH DESCRIPTION weblug is is a configurable webhook receiver that allows to define custom programs and script to be executed when a webhook is triggered. The configuration happens via yaml files. weblug supports multiple webhooks, limitations for concurrent web hooks to be executed, background execution and running webhooks as separate user (uid/gid) and basic auth. The system daemon uses the /etc/weblug.yml file. To enable the daemon, edit /etc/weblug.yml to your needs and then simply start/enable the system service. .SH OPTIONS .TP .B -h|--help Print help message .SH CONFIGURATION FILES .TP weblug needs a configuration file with webhook definitions to run. The program needs at least one configuration file, multiple files are supported. See the following example configuration file: .B "--- .br .B "## Weblug example config .br .B "settings: .br .B " #bind: "127.0.0.1:2088" # bind address for webserver .br .B " bind: ":2088" # bind to all addresses .br .B "# hook definitions. A hook needs to define the HTTP endpoint ("route") and the command .br .B "# See the following examples for more possible options. .br .B "hooks: .br .B " - name: 'hook one' .br .B " route: "/webhooks/1" .br .B " command: "sleep 5" .br .B " background: True # Terminate http request immediately .br .B " concurrency: 2 # At most 2 parallel processes are allowed .br .br .B " - name: 'hook two' .br .B " route: "/webhooks/2" .br .B " command: "bash -c 'sleep 5'" .br .B " concurrency: 5 # At most 5 parallel processes are allowed .br .br .br .B " - name: 'hook 3' .br .B " route: "/webhooks/data/3" .br .B " command: "bash -c 'echo $UID $GID'" .br .B " uid: 100 # Run command as system user id (uid) 100 .br .B " gid: 200 # Run command with system group id (gid) 200 .br .B " concurrency: 1 # No concurrency. Returns 500 on parallel requests .br .B " output: True # Print program output to console .br .br .br .B " - name: 'hook 4' .br .B " route: "/webhooks/restricted/4" .br .br .B " command: "true" .br .B " # Allow only requests from localhost .br .B " allowed: ["127.0.0.1/8", "::1/128"] .br .br .br .B " - name: 'hook 5' .br .B " route: "/webhooks/restricted/5" .br .B " command: "true" .br .B " # Allow everything, except those two subnets .br .B " blocked: ["192.168.0.0/16", "10.0.0.0/8"] .br .br .B " - name: 'hook auth' .br .B " route: "/webhooks/restricted/auth" .br .B " command: "true" .br .B " # Require basic auth for this webhook .br .B " basic_auth: .br .B " # Username is optional. If defined, the following username must match .br .B " # If not defined, any user will be accepted .br .B " username: 'user' .br .B " # Password is obligatory to enable basic_auth. If defined, a request must authenticate with the given password (cleartext) .br .B " password: 'password' .br 0707010000000B000081A4000000000000000000000001644A5E8E0000003D000000000000000000000000000000000000001200000000weblug-0.3/go.modmodule weblug/m/v2 go 1.18 require gopkg.in/yaml.v2 v2.4.0 0707010000000C000081A4000000000000000000000001644A5E8E00000168000000000000000000000000000000000000001200000000weblug-0.3/go.sumgopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 0707010000000D000041ED000000000000000000000001644A5E8E00000000000000000000000000000000000000000000001000000000weblug-0.3/test0707010000000E000081ED000000000000000000000001644A5E8E000005C5000000000000000000000000000000000000001C00000000weblug-0.3/test/blackbox.sh#!/bin/bash # Blackbox tests for weblug cleanup() { # kill all processes whose parent is this process pkill -P $$ } trap cleanup EXIT if [[ $EUID != 0 && $UID != 0 ]]; then echo -e "(!!) WARNING: Cannot UID and GID webhook tests, because we're not running as root (!!)\n" echo -e "Continuing in 3 seconds\n\n\n" sleep 3 fi rm -f testfile ../weblug test.yaml & sleep 1 ## Check touch webhook, which creates "testfile" echo -e "\n\nChecking 'testfile' webhook ... " curl http://127.0.0.1:2088/webhooks/touch if [[ ! -f testfile ]]; then echo "Testfile doesn't exist after running webhook touch" exit 1 fi rm -f testfile ## Check background webhook, that sleeps for 5 seconds but returns immediately echo -e "\n\nChecking 'background' webhook ... " timeout 2 curl http://127.0.0.1:2088/webhooks/background ## Check concurrency webhook, that allows only 2 requests at the same time (but sleeps for 5 seconds) echo -e "\n\nChecking 'concurrency' webhook ... " timeout 10 curl http://127.0.0.1:2088/3 & timeout 10 curl http://127.0.0.1:2088/3 & ! timeout 2 curl http://127.0.0.1:2088/3 ## Check UID and GID webhooks, but only if we're root echo -e "\n\nChecking 'uid/gid' webhook ... " # Skip this test, if we're not root if [[ $EUID == 0 || $UID == 0 ]]; then curl http://127.0.0.1:2088/webhooks/uid curl http://127.0.0.1:2088/webhooks/gid else echo "Cannot UID and GID webhook tests, because we're not running as root" fi echo -e "\n\nall good" 0707010000000F000081A4000000000000000000000001644A5E8E000002BB000000000000000000000000000000000000001A00000000weblug-0.3/test/test.yaml--- settings: bind: "127.0.0.1:2088" # bind address for webserver hooks: - name: 'touch hook' route: "/webhooks/touch" command: "touch testfile" - name: 'hook background' route: "/webhooks/background" command: "sleep 5" background: True - name: 'hook three' route: "/3" command: "sleep 5" concurrency: 2 - name: 'hook uid' route: "/webhooks/uid" command: "bash -c 'echo uid=$UID gid=$GID; if [[ $UID != 10 ]]; then exit 1; fi'" uid: 10 output: True - name: 'hook gid' route: "/webhooks/gid" command: "bash -c 'GID=`id -g`; echo uid=$UID gid=$GID; if [[ $GID != 10 ]]; then exit 1; fi'" uid: 10 gid: 10 output: True 07070100000010000081A4000000000000000000000001644A5E8E000000DA000000000000000000000000000000000000001A00000000weblug-0.3/weblug.service[Unit] Description=weblug - webhook Service After=network.target [Service] Type=simple WorkingDirectory=/var/lib/empty ExecStart=/usr/bin/weblug /etc/weblug.yml Restart=on-failure [Install] WantedBy=multi-user.target07070100000011000081A4000000000000000000000001644A5E8E00000714000000000000000000000000000000000000001600000000weblug-0.3/weblug.yml--- ## Weblug example config settings: #bind: "127.0.0.1:2088" # bind address for webserver bind: ":2088" # bind to all addresses # hook definitions. A hook needs to define the HTTP endpoint ("route") and the command # See the following examples for more possible options. hooks: - name: 'hook one' route: "/webhooks/1" command: "sleep 5" background: True # Terminate http request immediately concurrency: 2 # At most 2 parallel processes are allowed - name: 'hook two' route: "/webhooks/2" command: "bash -c 'sleep 5'" concurrency: 5 # At most 5 parallel processes are allowed - name: 'hook 3' route: "/webhooks/data/3" command: "bash -c 'echo $UID $GID'" uid: 100 # Run command as system user id (uid) 100 gid: 200 # Run command with system group id (gid) 200 concurrency: 1 # No concurrency. Returns 500 on parallel requests output: True # Print program output to console - name: 'hook 4' route: "/webhooks/restricted/4" command: "true" # Allow only requests from localhost allowed: ["127.0.0.1/8", "::1/128"] - name: 'hook 5' route: "/webhooks/restricted/5" command: "true" # Allow everything, except those two subnets blocked: ["192.168.0.0/16", "10.0.0.0/8"] - name: 'hook auth' route: "/webhooks/restricted/auth" command: "true" # Require basic auth for this webhook basic_auth: # Username is optional. If defined, the following username must match # If not defined, any user will be accepted username: 'user' # Password is obligatory to enable basic_auth. If defined, a request must authenticate with the given password (cleartext) password: 'password' 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!51 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor