diff --git a/PUBLIC_KEY_AUTH.md b/PUBLIC_KEY_AUTH.md new file mode 100644 index 00000000..9bd856ab --- /dev/null +++ b/PUBLIC_KEY_AUTH.md @@ -0,0 +1,3 @@ +`sudo ./__main__.py --clean --isfrontend --authdomain=./scripts/demo_public_key_auth_app.py` + +`./__main__.py --clean --publickeyauth=scripts/example-public-key-auth.sh --frontend=127.0.0.1:80 --service_on=https:test.example.com:localhost:3000:xxx` \ No newline at end of file diff --git a/doc/MANPAGE.md b/doc/MANPAGE.md index 79634a4e..e69de29b 100644 --- a/doc/MANPAGE.md +++ b/doc/MANPAGE.md @@ -1,528 +0,0 @@ -## Name ## - -pagekite - Make localhost servers publicly visible - -## Synopsis ## - -pagekite [`--options`] [`service`] `kite-name` [`+flags`] - -## Description ## - -PageKite is a system for exposing `localhost` servers to the -public Internet. It is most commonly used to make local web servers or -SSH servers publicly visible, although almost any TCP-based protocol can -work if the client knows how to use an HTTP proxy. - -PageKite uses a combination of tunnels and reverse proxies to compensate -for the fact that `localhost` usually does not have a public IP -address and is often subject to adverse network conditions, including -aggressive firewalls and multiple layers of NAT. - -This program implements both ends of the tunnel: the local "back-end" -and the remote "front-end" reverse-proxy relay. For convenience, -pagekite also includes a basic HTTP server for quickly exposing -files and directories to the World Wide Web for casual sharing and -collaboration. - -## Basic usage ## - -
Basic usage, gives `http://localhost:80/` a public name:
-$ pagekite NAME.pagekite.me
-
-To expose specific folders, files or use alternate local ports:
-$ pagekite /a/path/ NAME.pagekite.me +indexes  # built-in HTTPD
-$ pagekite *.html   NAME.pagekite.me           # built-in HTTPD
-$ pagekite 3000     NAME.pagekite.me           # HTTPD on 3000
-
-To expose multiple local servers (SSH and HTTP):
-$ pagekite ssh://NAME.pagekite.me AND 3000 NAME.pagekite.me
- -## Services and kites ## - -The most comman usage of pagekite is as a back-end, where it -is used to expose local services to the outside world. - -Examples of services are: a local HTTP server, a local SSH server, -a folder or a file. - -A service is exposed by describing it on the command line, along with the -desired public kite name. If a kite name is requested which does not already -exist in the configuration file and program is run interactively, the user -will be prompted and given the option of signing up and/or creating a new -kite using the pagekite.net service. - -Multiple services and kites can be specified on a single command-line, -separated by the word 'AND' (note capital letters are required). -This may cause problems if you have many files and folders by that -name, but that should be relatively rare. :-) - -## Kite configuration ## - -The options --list, --add, --disable and --remove can be used to -manipulate the kites and service definitions in your configuration file, -if you prefer not to edit it by hand. Examples: - -
Adding new kites
-$ pagekite --add /a/path/ NAME.pagekite.me +indexes
-$ pagekite --add 80 OTHER-NAME.pagekite.me
-
-To display the current configuration
-$ pagekite --list
-
-Disable or delete kites (--add re-enables)
-$ pagekite --disable OTHER-NAME.pagekite.me
-$ pagekite --remove NAME.pagekite.me
- -## Flags ## - -Flags are used to tune the behavior of a particular kite, for example -by enabling access controls or specific features of the built-in HTTP -server. - -### Common flags ### - - * +ip/`1.2.3.4` - Enable connections only from this IP address. - * +ip/`1.2.3` - Enable connections only from this /24 netblock. - -### HTTP protocol flags ### - - * +password/`name`=`pass` - Require a username and password (HTTP Basic Authentication) - - * +rewritehost - Rewrite the incoming Host: header. - * +rewritehost=`N` - Replace Host: header value with N. - * +rawheaders - Do not rewrite (or add) any HTTP headers at all. - * +proxyproto - Use HAProxy's PROXY Protocol (v1) to relay IPs etc. - * +insecure - Allow access to phpMyAdmin, /admin, etc. (per kite). - -### Built-in HTTPD flags ### - - * +indexes - Enable directory indexes. - * +indexes=`all` - Enable directory indexes including hidden (dot-) files. - * +hide - Obfuscate URLs of shared files. - * +uploads - Accept file uploads. - * +uploads=`RE` - Accept uploads to paths matching regexp RE. - * +ul_filenames=`P` - Upload naming policy. P = overwrite, keep or rename - - * +cgi=`list` - A list of extensions, for which files should be treated as - CGI scripts (example: `+cgi=cgi,pl,sh`). - - * +photobackup=`password` - Enable built-in PhotoBackup server with the given password. - See https://photobackup.github.io/ for details. - -## Options ## - -The full power of pagekite lies in the numerous options which -can be specified on the command line or in a configuration file (see below). - -Note that many options, especially the service and domain definitions, -are additive and if given multiple options the program will attempt to -obey them all. Options are processed in order and if they are not -additive then the last option will override all preceding ones. - -Although pagekite accepts a great many options, most of the -time the program defaults will Just Work. - -### Common options ### - - * --clean - Skip loading the default configuration file. - * --signup - Interactively sign up for pagekite.net service. - * --defaults - Set defaults for use with pagekite.net service. - * --whitelabel=D - Set defaults for pagekite.net white-labels. - * --whitelabels=D - Set defaults for pagekite.net white-labels (with TLS). - * --nocrashreport - Don't send anonymous crash reports to pagekite.net. - -### Back-end options ### - - * --shell - Run PageKite in an interactive shell. - * --nullui - Silent UI for scripting. Assumes Yes on all questions. - - * --list - List all configured kites. - * --add - Add (or enable) the following kites, save config. - * --remove - Remove the following kites, save config. - * --disable - Disable the following kites, save config. - * --only - Disable all but the following kites, save config. - - * --insecure - Allow access to phpMyAdmin, /admin, etc. (global). - - * --local=`ports` - Configure for local serving only (no remote front-end). - * --watch=`N` - Display proxied data (higher N = more verbosity). - - * --noproxy - Ignore system (or config file) proxy settings. - - * --proxy=`type`:`server`:`port`, --socksify=`server`:`port`, --torify=`server`:`port`
- Connect to the front-ends using SSL, an HTTP proxy, a SOCKS proxy, - or the Tor anonymity network. The type can be any of 'ssl', 'http' - or 'socks5'. The server name can either be a plain hostname, - user@hostname or user:password@hostname. For SSL connections the - user part may be a path to a client cert PEM file. If multiple - proxies are defined, they will be chained one after another. - - * --service_on=`proto`:`kitename`:`host`:`port`:`secret`
- Explicit configuration for a service kite. Generally kites are - created on the command-line using the service short-hand - described above, but this syntax is used in the config file. - The kitename `unknown`, if allowed by the front-end, represents - a backend of last resort for requests with no other match. - - * --authdomain=`DNS-suffix`, --authdomain=`/path/to/app`, --authdomain=`kite-domain`:`DNS-suffix`, --authdomain=`kite-domain`:`/path/to/app`
- Use `DNS-suffix` for remote DNS-based authentication of - incoming tunnel requests, or invoke an external application - for this purpose. If no kite-domain is given, use - this as the default authentication method. See the section - below on tunnel authentication for further details. In order - for the app path to be recognized as such, it must contain at - least one / character. - - * --auththreads=`N`
- Start N threads to process auth requests. Default is 1. - - * --authfail_closed
- If authentication fails, reject tunnel requests. The default is - to fail open and allow tunnels if the auth checks are broken. - - - * --service_off=`proto`:`kitename`:`host`:`port`:`secret`
- Same as --service_on, except disabled by default. - - * --service_cfg=`...`, --webpath=`...`
- These options are used in the configuration file to store service - and flag settings (see above). These are both likely to change in - the near future, so please just pretend you didn't notice them. - - * --frontend=`host`:`port`
- Connect to the named front-end server. If this option is repeated, - multiple connections will be made. - - * --frontends=`num`:`dns-name`:`port`
- Choose `num` front-ends from the A records of a DNS domain - name, using the given port number. Default behavior is to probe - all addresses and use the fastest one. - - * --frontends=`num`:`@/path/to/file`:`port`
- Same as above, except the IP address list will be loaded from - a file (and reloaded periodically), instead of using DNS. - - * --nofrontend=`ip`:`port`
- Never connect to the named front-end server. This can be used to - exclude some front-ends from auto-configuration. - - * --fe_certname=`domain`
- Connect using SSL, accepting valid certs for this domain. If - this option is repeated, any of the named certificates will be - accepted, but the first will be preferred. - - * --fe_nocertcheck
- Connect using SSL/TLS, but do not verify the remote certificate. - This is largely insecure but still thwarts passive attacks and - prevents routers and firewalls from corrupting the PageKite tunnel. - - * --ca_certs=`/path/to/file`
- Path to your trusted root SSL certificates file. - - * --dyndns=`X`
- Register changes with DynDNS provider X. X can either be simply - the name of one of the 'built-in' providers, or a URL format - string for ad-hoc updating. - - * --keepalive=`N`
- Force traffic over idle tunnels every N seconds, to cope with - firewalls that kill idle TCP connections. Backend only: if set - to "auto" (the default), the interval will be adjusted - automatically in response to disconnects. - - * --all - Terminate early if any tunnels fail to register. - * --new - Don't attempt to connect to any kites' old front-ends. - * --noprobes - Reject all probes for service state. - -### Front-end options ### - - * --isfrontend - Enable front-end operation. - - * --domain=`proto,proto2,pN`:`domain`:`secret`
- Accept tunneling requests for the named protocols and specified - domain, using the given secret. A * may be used as a wildcard - for subdomains or protocols. This is for static configurations, - for dynamic access controls use the `--authdomain` mechanism. - The domain `unknown`, if configured, represents a backend of - last resort for incoming requests with no other match. - - * --authdomain=`DNS-suffix`, --authdomain=`/path/to/app`, --authdomain=`kite-domain`:`DNS-suffix`, --authdomain=`kite-domain`:`/path/to/app`
- Use `DNS-suffix` for remote DNS-based authentication of - incoming tunnel requests, or invoke an external application - for this purpose. If no kite-domain is given, use - this as the default authentication method. See the section - below on tunnel authentication for further details. In order - for the app path to be recognized as such, it must contain at - least one / character. - - * --auththreads=`N`
- Start N threads to process auth requests. Default is 1. - - * --authfail_closed
- If authentication fails, reject tunnel requests. The default is - to fail open and allow tunnels if the auth checks are broken. - - * --motd=`/path/to/motd`
- Send the contents of this file to new back-ends as a - "message of the day". - - * --host=`hostname` - Listen on the given hostname only. - * --ports=`list` - Listen on a comma-separated list of ports. - * --portalias=`A:B` - Report port A as port B to backends (because firewalls). - * --protos=`list` - Accept the listed protocols for tunneling. - - * --rawports=`list`
- Listen for raw connections these ports. The string '%s' - allows arbitrary ports in HTTP CONNECT. - - * --overload=`baseline`, --overload_cpu=`fraction, 0-1`, --overload_mem=`fraction, 0-1`
- Enable "overload" calculations, which cause the front-end to - recommend back-ends go elsewhere if possible, once connection - counts go above a certain number. The baseline is the initial - overload level, but it will be adjusted dynamically based on - load average (CPU use) and memory usage. This will really only - work well on Linux and if PageKite is the only thing happening - on the machine. Setting both fractions to 0 disables dynamic - scaling. - - * --overload_file=`/path/to/baseline/file`
- Path to a file, the contents of which overrides all overload - calculations. This can be used to manage load calculations - using an external process (or by hand, e.g. to prepare for - maintenance). Note that overload must specify a non-zero - baseline, otherwise this setting is ignored. - - * --ratelimit_ips=`IPs/seconds`, --ratelimit_ips=`kitename`:`IPs/seconds`
- Limit how many different clients (IPs) can request data from - a tunnel within a given window of time, e.g. 5/3600. This is - useful as either a crude form of DDoS mitigation, or as a - mechanism to make public kite services unusable for phishing. - Note that limits are enforced per-tunnel (not per kite), and - tunnels serving multiple kites will use the settings of the - strictest kite. Limits apply to subdomains as well. A single - IP may be counted more than once if request headers (such as - User-Agent) differ. - - * --accept_acl_file=`/path/to/file`
- Consult an external access control file before accepting an - incoming connection. Quick'n'dirty for mitigating abuse. The - format is one rule per line: `rule policy comment` where a - rule is an IP or regexp and policy is 'allow' or 'deny'. - - * --client_acl=`policy`:`regexp`, --tunnel_acl=`policy`:`regexp`
- Add a client connection or tunnel access control rule. - Policies should be 'allow' or 'deny', the regular expression - should be written to match IPv4 or IPv6 addresses. If defined, - access rules are checkd in order and if none matches, incoming - connections will be rejected. - - * --tls_default=`name`
- Default name to use for SSL, if SNI (Server Name Indication) - is missing from incoming HTTPS connections. - - * --tls_endpoint=`name`:`/path/to/file`
- Terminate SSL/TLS for a name using key/cert from a file. - -### System options ### - - * --optfile=`/path/to/file`
- Read settings from file X. Default is `~/.pagekite.rc`. - - * --optdir=`/path/to/directory`
- Read settings from `/path/to/directory/*.rc`, in - lexicographical order. - - * --savefile=`/path/to/file`
- Saved settings will be written to this file. - - * --save - Save the current configuration to the savefile. - - * --settings
- Dump the current settings to STDOUT, formatted as a configuration - file would be. - - * --nopyopenssl - Avoid use of the pyOpenSSL library (not in config file) - * --nossl - Avoid use SSL entirely (not allowed in config file) - - * --nozchunks - Disable zlib tunnel compression. - * --sslzlib - Enable zlib compression in OpenSSL. - * --buffers=`N` - Buffer at most N kB of data before blocking. - * --logfile=`F` - Log to file F, `stdio` means standard output. - * --daemonize - Run as a daemon. - * --runas=`U`:`G` - Set UID:GID after opening our listening sockets. - * --pidfile=`P` - Write PID to the named file. - * --errorurl=`U` - URL to redirect to when back-ends are not found. - * --errorurl=`D:U` - Custom error URL for domain D. - - * --selfsign
- Configure the built-in HTTP daemon for HTTPS, first generating a - new self-signed certificate using openssl if necessary. - - * --httpd=`X`:`P`, --httppass=`X`, --pemfile=`X`
- Configure the built-in HTTP daemon. These options are likely to - change in the near future, please pretend you didn't see them. - -## Configuration files ## - -If you are using pagekite as a command-line utility, it will -load its configuration from a file in your home directory. The file is -named `.pagekite.rc` on Unix systems (including Mac OS X), or -`pagekite.cfg` on Windows. - -If you are using pagekite as a system-daemon which starts up -when your computer boots, it is generally configured to load settings -from `/etc/pagekite.d/*.rc` (in lexicographical order). - -In both cases, the configuration files contain one or more of the same -options as are used on the command line, with the difference that at most -one option may be present on each line, and the parser is more tolerant of -white-space. The leading '--' may also be omitted for readability and -blank lines and lines beginning with '#' are treated as comments. - -NOTE: When using -o, --optfile or --optdir on the command line, -it is advisable to use --clean to suppress the default configuration. - -## Security ## - -Please keep in mind, that whenever exposing a server to the public -Internet, it is important to think about security. Hacked webservers are -frequently abused as part of virus, spam or phishing campaigns and in -some cases security breaches can compromise the entire operating system. - -Some advice:
-   * Switch PageKite off when not using it.
-   * Use the built-in access controls and SSL encryption.
-   * Leave the firewall enabled unless you have good reason not to.
-   * Make sure you use good passwords everywhere.
-   * Static content is very hard to hack!
-   * Always, always make frequent backups of any important work.
- -Note that as of version 0.5, pagekite includes a very basic -request firewall, which attempts to prevent access to phpMyAdmin and -other sensitive systems. If it gets in your way, the +insecure -flag or --insecure option can be used to turn it off. - -For more, please visit: - -## Tunnel Request Authentication ## - -When running pagekite as a front-end relay, you can enable -dynamic authentication of incoming tunnel requests in two ways. - -One uses a DNS-based protocol for delegating authentication to a remote -server. The nice thing about this, is relays can be deployed without -any direct access to your user account databases - in particular, a -zero-knowlege challenge/response protocol is used which means the relay -never sees the shared secret used to authenticate the kite. - -The second method delegates authentication to an external app; this -external app can be written in any language you like, as long as it -implements the following command-line arguments: -
-  --capabilities     Print a list of capabilities to STDOUT and exit
-  --server           Run as a "server", reading queries on STDIN and
-                  sending one-line replies to STDOUT.
-  --auth     Return JSON formatted auth and quota details
-  --zk-auth   Implement the DNS-based zero-knowlege protocol
-
-The recognized capabilities are SERVER, ZK-AUTH and AUTH. One of AUTH -or ZK-AUTH is required. - -The JSON `--auth` responses should be dictionaries which have at least -one element, `secret` or `error`. The secret is the shared secret to -be used to authenticate the tunnel. The dictionary may also contain -advisory quota values (`quota_kb`, `quota_days` and `quota_conns`), and -IP rate limiting parameters (`ips_per_sec-ips` and `ips_per_sec-secs`). - -The source distribution of pagekite includes a script named -`demo_auth_app.py` which implements this protocol. - -## Bugs ## - -Using pagekite as a front-end relay with the native Python SSL -module may result in poor performance. Please use the pyOpenSSL wrappers -instead. - -## See Also ## - -lapcat(1), , - -## Credits ## - -
- Bjarni R. Einarsson 
-- The Beanstalks Project ehf. 
-- The Rannis Technology Development Fund 
-- Joar Wandborg 
-- Luc-Pierre Terral
- -## Copyright and license ## - -Copyright 2010-2020, the Beanstalks Project ehf. and Bjarni R. Einarsson. - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or (at -your option) any later version. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public -License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see: - - diff --git a/pagekite/manual.py b/pagekite/manual.py index f91f5f6e..64628413 100755 --- a/pagekite/manual.py +++ b/pagekite/manual.py @@ -244,6 +244,9 @@ --all __Terminate early if any tunnels fail to register. --new __Don't attempt to connect to any kites' old front-ends. --noprobes __Reject all probes for service state. + --publickeyauth=/path/to/script __ + Use public key authentication with script + """) MAN_OPT_FRONTEND = ("""\ --isfrontend __Enable front-end operation. diff --git a/pagekite/pk.py b/pagekite/pk.py index 53444e0a..a5b24d83 100755 --- a/pagekite/pk.py +++ b/pagekite/pk.py @@ -88,6 +88,7 @@ 'logfile=', 'daemonize', 'nodaemonize', 'runas=', 'pidfile=', 'isfrontend', 'noisfrontend', 'settings', 'defaults', 'whitelabel=', 'whitelabels=', 'local=', 'domain=', + 'publickeyauth=', 'auththreads=', 'authdomain=', 'authfail_closed', 'motd=', 'register=', 'host=', 'noupgradeinfo', 'upgradeinfo=', 'ports=', 'protos=', 'portalias=', 'rawports=', @@ -1180,6 +1181,8 @@ def ResetConfiguration(self): self.kite_disable = False self.kite_remove = False + self.public_key_auth = None + # Searching for our configuration file! We prefer the documented # 'standard' locations, but if nothing is found there and something local # exists, use that instead. @@ -2577,6 +2580,7 @@ def Configure(self, argv): elif opt == '--defaults': self.SetServiceDefaults() elif opt == '--whitelabel': self.SetWhitelabelDefaults(arg, secure=False) elif opt == '--whitelabels': self.SetWhitelabelDefaults(arg, secure=True) + elif opt == '--publickeyauth': self.public_key_auth = arg elif opt in ('--clean', '--nopyopenssl', '--nossl', '--settings', '--signup', '--friendly'): # These are handled outside the main loop, we just ignore them. @@ -3566,7 +3570,9 @@ def iping(ip): def ConnectFrontend(self, conns, server): self.ui.Status('connect', color=self.ui.YELLOW, message='Front-end connect: %s' % server) - tun = Tunnel.BackEnd(server, self.backends, self.require_all, conns) + + print("PUBLIC_KEY_AUTH: " + self.public_key_auth) + tun = Tunnel.BackEnd(server, self.backends, self.require_all, conns, self.public_key_auth) if tun: tun.filters.append(HaproxyProtocolFilter(self.ui)) tun.filters.append(HttpHeaderFilter(self.ui)) diff --git a/pagekite/proto/conns.py b/pagekite/proto/conns.py index 727293de..95b5d282 100755 --- a/pagekite/proto/conns.py +++ b/pagekite/proto/conns.py @@ -632,7 +632,7 @@ def HandlePageKiteResponse(self, parse): return have_kite_info and have_kites - def _BackEnd(server, backends, require_all, conns): + def _BackEnd(server, backends, require_all, conns, public_key_auth): """This is the back-end end of a tunnel.""" self = Tunnel(conns) if conns and not conns.config.isfrontend: @@ -666,7 +666,7 @@ def _BackEnd(server, backends, require_all, conns): if tryagain: if self.server_info[self.S_ADD_KITES]: request = PageKiteRequestHeaders(server, conns.config.backends, - tokens) + tokens, public_key_auth=public_key_auth) abort = not self.SendChunked(('NOOP: 1\r\n%s\r\n\r\n!' ) % ''.join(request), compress=False, just_buffer=True) diff --git a/pagekite/proto/proto.py b/pagekite/proto/proto.py index 9c72d30f..ad2de21e 100755 --- a/pagekite/proto/proto.py +++ b/pagekite/proto/proto.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import division + LICENSE = """\ This file is part of pagekite.py. Copyright 2010-2020, the Beanstalks Project ehf. and Bjarni Runar Einarsson @@ -33,6 +34,11 @@ import struct import time +try: + import subprocess +except ImportError: + subprocess = None + from pagekite.compat import * from pagekite.common import * import pagekite.logging as logging @@ -66,7 +72,8 @@ def globalSecret(): TOKEN_LENGTH=36 def signToken(token=None, secret=None, payload='', timestamp=None, - length=TOKEN_LENGTH): + length=TOKEN_LENGTH, public_key_auth=None): + """ This will generate a random token with a signature which could only have come from this server. If a token is provided, it is re-signed so the original @@ -75,17 +82,31 @@ def signToken(token=None, secret=None, payload='', timestamp=None, If a timestamp is provided it will be embedded in the signature to a resolution of 10 minutes, and the signature will begin with the letter 't' + If the path to a script is passed in public_key_auth, this script will be called + with payload + ts as parameter and the output of the script will be returned. + Note: This is only as secure as random.randint() is random. """ if not secret: secret = globalSecret() - if not token: token = sha1hex('%s%8.8x' % (globalSecret(), - random.randint(0, 0x7FFFFFFD)+1)) - if timestamp: - tok = 't' + token[1:] - ts = '%x' % int(timestamp/600) # Integer division - return tok[0:8] + sha1hex(secret + payload + ts + tok[0:8])[0:length-8] + + + if public_key_auth: + signText = payload + if timestamp: + ts = '%x' % int(timestamp / 600) # Integer division + signText += ts + result = subprocess.check_output([public_key_auth, signText]).strip() else: - return token[0:8] + sha1hex(secret + payload + token[0:8])[0:length-8] + if not token: token = sha1hex('%s%8.8x' % (globalSecret(), + random.randint(0, 0x7FFFFFFD)+1)) + if timestamp: + tok = 't' + token[1:] + ts = '%x' % int(timestamp/600) # Integer division + result = tok[0:8] + sha1hex(secret + payload + ts + tok[0:8])[0:length-8] + else: + result = token[0:8] + sha1hex(secret + payload + token[0:8])[0:length-8] + + return result def checkSignature(sign='', secret='', payload=''): """ @@ -103,7 +124,7 @@ def checkSignature(sign='', secret='', payload=''): valid = signToken(token=sign, secret=secret, payload=payload) return sign == valid -def PageKiteRequestHeaders(server, backends, tokens=None, testtoken=None, replace=None): +def PageKiteRequestHeaders(server, backends, tokens=None, testtoken=None, replace=None, public_key_auth=None): req = ['X-PageKite-Version: %s\r\n' % APPVER] if replace: req.append('X-PageKite-Replace: %s\r\n' % replace) @@ -126,7 +147,8 @@ def PageKiteRequestHeaders(server, backends, tokens=None, testtoken=None, replac # Sign the payload with the shared secret (random salt). sign = signToken(secret=backends[d][BE_SECRET], payload=data, - token=testtoken) + token=testtoken, + public_key_auth=public_key_auth) req.append('X-PageKite: %s:%s\r\n' % (data, sign)) return req diff --git a/scripts/demo_public_key_auth_app.py b/scripts/demo_public_key_auth_app.py new file mode 100755 index 00000000..9e0895ff --- /dev/null +++ b/scripts/demo_public_key_auth_app.py @@ -0,0 +1,90 @@ +#!/usr/bin/python2 -u + +from __future__ import absolute_import + +# +# This is a trivial demo auth server, which just approves any requests +# it sees, while printing debug information to STDERR. +# +# This code is in the public domain, feel free to adapt to your needs. +# +import getopt +import json +import sys +import subprocess + + +CAPABILITIES = ('ZK-AUTH') + + +def ZkAuth(domain): + + parts = domain.split('.') + sys.stderr.write(str(parts) + '\n') + + jws = ".".join(parts[2:5]) + + tunnel_domain = '.'.join(parts[6:-4]) + + sys.stderr.write(jws + '\n' + tunnel_domain + '\n') + + jws_check_process = subprocess.Popen(['jose-util', 'verify', '--key=./scripts/jwk-sig-example.com-pub.json'],stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + jws_payload, _ = jws_check_process.communicate(input=jws) + + if jws_check_process.returncode != 0: + return {'hostname': domain, 'alias': '', 'ips': ['0.0.0.0']} + + sys.stderr.write(jws_payload + '\n') + + payload_dict = json.loads(jws_payload) + + if tunnel_domain != payload_dict.get('domain'): + sys.stderr.write('Domain mismatch!\n%s != %s' % (tunnel_domain, payload_dict.get('domain'))) + return {'hostname': domain, 'alias': '', 'ips': ['0.0.0.0']} + + return {'hostname': domain, 'alias': '', 'ips': ['255.255.255.0']} + + +def P(string): + # Delete the sys.stderr line if you're not debugging. + sys.stderr.write('>> ' + string + '\n') + print(string) + + +def ProcessArgs(args, server=False): + o, a = getopt.getopt(args, 'a:z:', + ([] if server else ['capabilities', 'server']) + + ['auth=', 'zk-auth=']) + + for opt, arg in o: + sys.stderr.write('<< %s=%s\n' % (opt, arg)) + + if opt == '--capabilities': + P(CAPABILITIES) + return + + if opt == '--server': + ServerLoop() + return + + if opt in ('-z', '--zk-auth'): + P(json.dumps(ZkAuth(arg), indent=None)) + return + + +def ServerLoop(): + while True: + line = sys.stdin.readline() + if not line: + return + + args = line.strip().split() + if args and not args[0][:2] == '--': + args[0] = '--' + args[0] + ProcessArgs(args, server=True) + + +if __name__ == '__main__': + ProcessArgs(sys.argv[1:]) diff --git a/scripts/example-public-key-auth.sh b/scripts/example-public-key-auth.sh new file mode 100755 index 00000000..d4fb16b6 --- /dev/null +++ b/scripts/example-public-key-auth.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +cat './scripts/jws-example-clear.json' | jose-util sign --key ./scripts/jwk-sig-example.com-priv.json --alg EdDSA diff --git a/scripts/jwk-sig-example.com-priv.json b/scripts/jwk-sig-example.com-priv.json new file mode 100644 index 00000000..6ff719b2 --- /dev/null +++ b/scripts/jwk-sig-example.com-priv.json @@ -0,0 +1,9 @@ +{ + "use": "sig", + "kty": "OKP", + "kid": "example.com", + "crv": "Ed25519", + "alg": "EdDSA", + "x": "FeCpsQsUAfhGDxrqXx6rjnD26gTaIB8dJDJNyasg7RI", + "d": "IGDhGoWn5OMFx9YWypRCG1XCWd7iRwucYgReobbqsV0" +} \ No newline at end of file diff --git a/scripts/jwk-sig-example.com-pub.json b/scripts/jwk-sig-example.com-pub.json new file mode 100644 index 00000000..3afefac4 --- /dev/null +++ b/scripts/jwk-sig-example.com-pub.json @@ -0,0 +1,8 @@ +{ + "use": "sig", + "kty": "OKP", + "kid": "example.com", + "crv": "Ed25519", + "alg": "EdDSA", + "x": "FeCpsQsUAfhGDxrqXx6rjnD26gTaIB8dJDJNyasg7RI" +} \ No newline at end of file diff --git a/scripts/jws-example-clear.json b/scripts/jws-example-clear.json new file mode 100644 index 00000000..f5b58a87 --- /dev/null +++ b/scripts/jws-example-clear.json @@ -0,0 +1,3 @@ +{ + "domain": "test.example.com" +} \ No newline at end of file