GithubHelp home page GithubHelp logo

ngrok / ngrok-python Goto Github PK

View Code? Open in Web Editor NEW
105.0 6.0 20.0 1.79 MB

Embed ngrok secure ingress into your Python apps with a single line of code.

Home Page: https://ngrok.com

License: Apache License 2.0

Makefile 3.12% Python 34.37% Nix 2.50% Rust 60.01%
networking ngrok python python3 reverse-proxy

ngrok-python's Introduction

Python SDK for ngrok

PyPI Supported Versions MIT licensed Apache-2.0 licensed Continuous integration

ngrok-python is the official Python SDK for ngrok that requires no binaries. Quickly enable secure production-ready connectivity to your applications and services directly from your code.

ngrok is a globally distributed gateway that provides secure connectivity for applications and services running in any environment.

Installation

The ngrok-python SDK can be installed from PyPI via pip:

pip install ngrok

Quickstart

  1. Install ngrok-python

  2. Export your authtoken from the ngrok dashboard as NGROK_AUTHTOKEN in your terminal

  3. Add the following code to your application to establish connectivity via the forward method through port 9000 on localhost:

    # import ngrok python sdk
    import ngrok
    
    # Establish connectivity
    listener = ngrok.forward(9000, authtoken_from_env=True)
    
    # Output ngrok url to console
    print(f"Ingress established at {listener.url()}")

That's it! Your application should now be available through the url output in your terminal.

Note You can find more examples in the examples directory.

Documentation

A full quickstart guide and API reference can be found in the ngrok-python documentation.

Authentication

To use most of ngrok's features, you'll need an authtoken. To obtain one, sign up for free at ngrok.com and retrieve it from the authtoken page in your ngrok dashboard. Once you have copied your authtoken, you can reference it in several ways.

You can set it in the NGROK_AUTHTOKEN environment variable and pass authtoken_from_env=True to the forward method:

ngrok.forward(authtoken_from_env=True, ...)

Or pass the authtoken directly to the forward method:

ngrok.forward(authtoken=token, ...)

Or set it for all connections with the set_auth_token method:

ngrok.set_auth_token(token)

Connection

The forward method is the easiest way to start an ngrok session and establish a listener to a specified address. If an asynchronous runtime is running, the forward method returns a promise that resolves to the public listener object.

With no arguments, the forward method will start an HTTP listener to localhost port 80:

listener = ngrok.forward()

You can pass the port number to forward on localhost:

listener = ngrok.forward(4242)

Or you can specify the host and port via a string:

listener = ngrok.forward("localhost:4242")

More options can be passed to the forward method to customize the connection:

listener = ngrok.forward(8080, basic_auth="ngrok:online1line"})
listener = ngrok.forward(8080, oauth_provider="google", oauth_allow_domains="example.com")

The second (optional) argument is the listener type, which defaults to http. To create a TCP listener:

listener = ngrok.forward(25565, "tcp")

Since the options are kwargs, you can also use the ** operator to pass a dictionary for configuration:

options = {"authtoken_from_env":True, "response_header_add":"X-Awesome:yes"}
listener = ngrok.forward(8080, **options)

See Full Configuration for the list of possible configuration options.

Disconnection

To close a listener use the disconnect method with the url of the listener to close. If there is an asynchronous runtime running the disconnect method returns a promise that resolves when the call is complete.

ngrok.disconnect(url)

Or omit the url to close all listeners:

ngrok.disconnect()

The close method on a listener will shut it down, and also stop the ngrok session if it is no longer needed. This method returns a promise that resolves when the listener is closed.

await listener.close()

List all Listeners

To list all current non-closed listeners use the get_listeners method. If there is an asynchronous runtime running the get_listeners method returns a promise that resolves to the list of listener objects.

listeners = ngrok.get_listeners()

TLS Backends

As of version 0.10.0 there is backend TLS connection support, validated by a filepath specified in the SSL_CERT_FILE environment variable, or falling back to the host OS installed trusted certificate authorities. So it is now possible to do this to connect:

ngrok.forward("https://127.0.0.1:3000", authtoken_from_env=True)

If the service is using certs not trusted by the OS, such as self-signed certificates, add an environment variable like this before running: SSL_CERT_FILE=/path/to/ca.crt. There is also a verify_upstream_tls=False option to disable certification verification.

Unix Sockets

You may also choose to use Unix Sockets instead of TCP. You can view an example of this here.

A socket address may be passed directly into the listener forward() call as well by prefixing the address with unix:, for example unix:/tmp/socket-123.

Builders

For more control over Sessions and Listeners, the builder classes can be used.

A minimal example using the builder class looks like the following:

async def create_listener():
    session = await ngrok.NgrokSessionBuilder().authtoken_from_env().connect()
    listener = await session.http_endpoint().listen()
    print (f"Ingress established at {listener.url()}")
    listener.forward("localhost:9000")

See here for a Full Configuration Example

Full Configuration

This example shows all the possible configuration items of ngrok.forward:

listener = ngrok.forward(
    # session configuration
    addr="localhost:8080",
    authtoken="<authtoken>",
    authtoken_from_env=True,
    app_protocol="http2",
    session_metadata="Online in One Line",
    # advanced session connection configuration
    server_addr="example.com:443",
    root_cas="trusted",
    session_ca_cert=load_file("ca.pem"),
    # listener configuration
    metadata="example listener metadata from python",
    domain="<domain>",
    schemes=["HTTPS"],
    proto="http",
    proxy_proto="",  # One of: "", "1", "2"
    labels="edge:edghts_2G...",  # Along with proto="labeled"
    # module configuration
    basic_auth=["ngrok:online1line"],
    circuit_breaker=0.1,
    compression=True,
    allow_user_agent="^mozilla.*",
    deny_user_agent="^curl.*",
    allow_cidr="0.0.0.0/0",
    deny_cidr="10.1.1.1/32",
    crt=load_file("crt.pem"),
    key=load_file("key.pem"),
    mutual_tls_cas=load_file("ca.crt"),
    oauth_provider="google",
    oauth_allow_domains=["<domain>"],
    oauth_allow_emails=["<email>"],
    oauth_scopes=["<scope>"],
    oauth_client_id="<id>",
    oauth_client_secret="<id>",
    oidc_issuer_url="<url>",
    oidc_client_id="<id>",
    oidc_client_secret="<secret>",
    oidc_allow_domains=["<domain>"],
    oidc_allow_emails=["<email>"],
    oidc_scopes=["<scope>"],
    policy="<policy_json>",
    request_header_remove="X-Req-Nope",
    response_header_remove="X-Res-Nope",
    request_header_add="X-Req-Yup:true",
    response_header_add="X-Res-Yup:true",
    verify_upstream_tls=False,
    verify_webhook_provider="twilio",
    verify_webhook_secret="asdf",
    websocket_tcp_converter=True,
)

ASGI Runner

ngrok-python comes bundled with an ASGI (Asynchronous Server Gateway Interface) runner ngrok-asgi that can be used for Uvicorn, Gunicorn, Django and more, with no code.

To use prefix your start up command for a Uvicorn or Gunicorn web server with either ngrok-asgi or python -m ngrok.

Any TCP or Unix Domain Socket arguments will be used to establish connectivity automatically. The ngrok listener can be configured using command flags, for instance adding --basic-auth ngrok online1line will introduce basic authentication to the ingress listener.

Uvicorn

# Basic Usage
ngrok-asgi uvicorn mysite.asgi:application

# With custom host and port
ngrok-asgi uvicorn mysite.asgi:application \
    --host localhost \
    --port 1234

# Using basic auth
ngrok-asgi uvicorn mysite.asgi:application \
    --host localhost \
    --port 1234 \
    --basic-auth ngrok online1line

# Using custom sock file
ngrok-asgi uvicorn mysite.asgi:application \
    --uds /tmp/uvicorn.sock

# Using module name
python -m ngrok uvicorn mysite.asgi:application \
    --oauth-provider google \
    --allow-emails [email protected]

Gunicorn

# Basic Usage
ngrok-asgi gunicorn mysite.asgi:application -k uvicorn.workers.UvicornWorker

# With custom host and port
ngrok-asgi gunicorn mysite.asgi:application -k uvicorn.workers.UvicornWorker \
    --bind localhost:1234

# Using webhook verifications
ngrok-asgi gunicorn mysite.asgi:application -k uvicorn.workers.UvicornWorker \
    --webhook-verification twilio s3cr3t

# Using custom sock file
ngrok-asgi gunicorn mysite.asgi:application -k uvicorn.workers.UvicornWorker \
    --bind unix:/tmp/gunicorn.sock

# Using module name
python -m ngrok gunicorn mysite.asgi:application -k uvicorn.workers.UvicornWorker --response-header X-Awesome True

Examples

Listeners

Frameworks

Machine Learning

Platform Support

Pre-built binaries are provided on PyPI for the following platforms:

OS i686 x64 aarch64 arm
Windows *
MacOS
Linux
Linux musl
FreeBSD *

Note ngrok-python, and ngrok-rust which it depends on, are open source, so it may be possible to build them for other platforms.

  • Windows-aarch64 will be supported after the next release of Ring.
  • FreeBSD-x64 is built by the release process, but PyPI won't accept BSD flavors.

Dependencies

  • This project relies on PyO3, an excellent system to ease development and building of Rust plugins for Python.
  • Thank you to OpenIoTHub for handing over the ngrok name on PyPI.

Changelog

Changes to ngrok-python are tracked under CHANGELOG.md.

Join the ngrok Community

License

This project is dual-licensed under Apache, Version 2.0 and MIT. You can choose between one of them if you use this work.

Contributions

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in ngrok-python by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Development: Getting Started

Prerequisites:

  • a valid Ngrok authtoken
  • make available in your PATH
  1. Update Cargo.toml with the latest supported ngrok = { version = "=VERSION_HERE" } from ngrok-rust. ngrok-rust is used for the bindings in src/rust_files_here.rs

  2. Run make build (builds the rust bindings / python dependencies)

  3. Happy developing!


Example Commands:

building the project

make develop

running the entire test suite

# running the entire test suite
export NGROK_AUTHTOKEN="YOUR_AUTHTOKEN_HERE"; make test

running an individual test

# running an individual test
export NGROK_AUTHTOKEN="YOUR_AUTHTOKEN_HERE"; make test="-k TEST_CLASS.NAME_OF_TEST" test

See the MakeFile for more examples

HTTP2

The examples include a minimal hypercorn HTTP/2 example if you run make http2. You can curl the endpoint logged with INFO:ngrok.listener:Created and verify the HTTP/2 response from hypercorn.

curl --http2 -v https://<YOUR_LISTENER_URL>
*   Trying <YOUR_IP>:443...
* Connected to a6278d6c07ce.ngrok.app (<YOUR_IP>) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
...
> GET / HTTP/2
> Host: a6278d6c07ce.ngrok.app
> user-agent: curl/7.81.0
> accept: */*
>
...
< HTTP/2 200
< content-type: text/plain
< date: Fri, 01 Mar 2024 18:50:23 GMT
< ngrok-agent-ips: <YOUR_AGENT_IP>
< ngrok-trace-id: ed038ace04876818149cf0769bd43e38
< server: hypercorn-h2
<
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection #0 to host <YOUR_LISTENER_URL> left intact
hello

ngrok-python's People

Contributors

abdiramen avatar bobzilladev avatar carlamko avatar kristopherpaulsen avatar nijikokun avatar ofthedelmer 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

ngrok-python's Issues

ngrok and ssl certs

Running flask as

app.run(debug=True, use_reloader=True, port=port, ssl_context=("certs/cert.pem", "certs/key.pem")

Doesn't appear as connect / http_endpoint support SSL certs locally?

ngrok http https://127.0.0.1:3000/ 

Works fine

Can we add this to http_endpoint?

Websocket Connection

I have a particular usecase where I am trying to send data from my JS front end to my Python backend via websockets, exposed through ngrok.

Python Server:

async def handler(websocket):
    while True:
        message = await websocket.recv()
        print(message)

async def main():
    # Start WebSocket server
    async with websockets.serve(handler, args.host, args.port):
        await asyncio.Future()  # run forever


if __name__ == "__main__":
    ngrok_tunnel = ngrok.forward(args.port, "tcp", authtoken_from_env=True)
    print(f"Ngrok tunnel established at {ngrok_tunnel.url()}")
    asyncio.run(main())

JS Front End:

import { useState, useRef, useEffect } from "react";

const Test = () => {
  const [text, setText] = useState("");
  const [isCreateEvent, setIsCreateEvent] = useState(false);
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const newSocket = new WebSocket("ws://5.tcp.eu.ngrok.io:12075");
    setSocket(newSocket);
  }, []);

  useEffect(() => {
    if (socket && socket.readyState === WebSocket.OPEN) {
      socket.on("message", (event) => {
        // Update the text state based on the message received
        console.log({ event });
        const data = JSON.parse(event.data);
        setText((prev) => `${prev}\n${data.data}`);
      });
    }
  }, [socket]);
  const startCreateEvent = () => {
    const event = { data: "1" };
    socket.send(JSON.stringify(event));
  };

  const stopCreateEvent = async () => {
    await socket.close();
    setSocket(null);
  };


  return (
    <div>
      <h1>Test Socket</h1>
      <textarea value={text} readOnly />
      <button onClick={startCreateEvent} disabled={isCreateEvent}>
        Start 
      </button>
      <button onClick={stopCreateEvent} disabled={!isCreateEvent}>
        Stop 
      </button>
    </div>
  );
};

export default Test;

Error, on connection:

Traceback (most recent call last):
  File "/Users/miniconda3/envs/venv/lib/python3.10/site-packages/websockets/legacy/server.py", line 236, in handler
    await self.ws_handler(self)
  File "/Users/Desktop/server.py", line 126, in handler
    message = await websocket.recv()
  File "/Users/miniconda3/envs/venv/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 568, in recv
    await self.ensure_open()
  File "/Users//miniconda3/envs/venv/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 948, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: sent 1011 (internal error) keepalive ping timeout; no close frame received

Note that this doesn't occur when I don't use NGROK, leading me to believe there is some error in the way the port is being forwarded.

I've tried the same with tcp and http.

Failed to install ping when attempting to install ngrok with pip

Failed to install ping from https://files.pythonhosted.org/packages/03/ac/9a3f332f8d7d27cd8929922b2e225842c1623760364b37fc79dc0f65e77d/ping-0.2.tar.gz#sha256=ecb32294c2af8ae075de4a3743f568db376480ad81c2e010a7f1ce1cee7b030f (from ngrok).

Process 'command 'C:/Users/....../AppData/Local/Programs/Python/Python39/python.exe'' finished with non-zero exit value 1

I tried to install ngrok for my app, but suddenly it crashes when I tried to compile as a standalone app

Ngrok Tunnel from Google Colab not working

Hi everyone,
I'm just getting started with Google Colab and NGrok to test an application, and I'm using a basic Flask example.

I've initiated a tunnel using an auth token, and when I visit https://dashboard.ngrok.com/tunnels/agents, I can see that the tunnel has started successfully. It looks like ngrok and pyngrok are doing what they're supposed to do.

It seems like there might be some issue with the origin of Colab being blocked and it's not possible to access the expose URL.
Does anyone have a simple working example they could share with me?

This is the content of the Cells created at Google Colab:
#CELL1
Cell1

#CELL2
Cell2

how to pass edge/labels and other tunnel/forwarding params in the Django example?

I found a stub/placeholder document for using ngrok with django and also found a django example here in the sources.

However, I couldn't figure out how/where to add labels='edge:edge_yadayada...' into the configuration for the listener.

Neither the ngrok.default() nor the listener.forward() methods (shown below, in a snippet from the django example) support a labels argument. Where would these various parameters be passed, which are generally designed to be passed to ngrok.forward()?

...
listener = await ngrok.default()
...
listener.forward(listen)
...

Thanks,

Tunnel don't disconnecting

`
mport ngrok, time

ngrok.set_auth_token(Ngrok token)

listener = ngrok.connect(1194)

print(listener.url())

input()

print("tunnel stoping")

listener = ngrok.disconnect

time.sleep(60)
`

The tunnel turns on successfully, but cannot turn off.

TLS handshake error

HI, I'm new in ngrok, and I'm trying to implement the example code in raspberry pi.

import ngrok
import os

if "NGROK_AUTHTOKEN" not in os.environ:
    print("Please set the NGROK_AUTHTOKEN environment variable.")
    exit(1)
else:
    print("NGROK_AUTHTOKEN is set.")
token = os.environ["NGROK_AUTHTOKEN"]
tunnel = ngrok.connect(9000, "HTTP", authtoken=token)
print (f"Ingress established at {tunnel.url()}")

also this version

import ngrok
import os

if "NGROK_AUTHTOKEN" not in os.environ:
    print("Please set the NGROK_AUTHTOKEN environment variable.")
    exit(1)
else:
    print("NGROK_AUTHTOKEN is set.")

tunnel = ngrok.connect(9000, authtoken_from_env=True)
print (f"Ingress established at {tunnel.url()}")

These two version shows me the same error, I have tried to do some research these days and still can't solve this issue.
Am I missing something need to set up, before I use the example code?

 NGROK_AUTHTOKEN is set.
Sending fatal alert BadCertificate
Traceback (most recent call last):
  File "/home/pi/cageControllerInRas/testNgrok.py", line 10, in <module>
    tunnel = ngrok.connect(9000, "http", authtoken=token)
  File "ngrok_wrapper", line 16, in run
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "ngrok_wrapper", line 7, in wrap
ValueError: ('failed to connect session', 'tls handshake error')

How do I get a list of tunnels by calling `ngrok.get_listeners()` from async?

How do I get a list of tunnels by calling ngrok.get_listeners() from async?

import ngrok
import asyncio


def get_listeners():
    print(ngrok.get_listeners())


async def async_get_listeners():
    print(ngrok.get_listeners())


get_listeners()

asyncio.run(async_get_listeners())

Output:

[]
<Task pending name='Task-5' coro=<wrap() running at ngrok_wrapper:6>>

aiohttp.client_exceptions.ClientOSError: [Errno 1] [SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:2548)

Windows 10
Python 3.10.9

package versions:
aiohttp==3.9.3
fastapi==0.110.0
uvicorn[standard]==0.28.0
ngrok==1.2.0

client.py:

import aiohttp
import asyncio
from source import config

path = f"https://{config.DOMAIN}/some_long_func"


async def main():
    async with aiohttp.ClientSession() as session:
        async with session.request("post", path, json={"some": "message"}) as response:
            print(await response.json())


asyncio.run(main())

api.py:

import ngrok
import uvicorn
import asyncio
from source import config
from fastapi import FastAPI, Request


app = FastAPI()


@app.post('/some_long_func')
async def some_long_func(request: Request):
    print(await request.json())
    await asyncio.sleep(60 * 5)
    return await request.json()


ngrok_tunnel = ngrok.forward(config.API_PORT, authtoken=config.NGROK_AUTHTOKEN, domain=config.DOMAIN)

print(f"Public URL: {ngrok_tunnel.url()}")


if __name__ == "__main__":
    uvicorn.run(app, port=config.API_PORT)

Error:

Traceback (most recent call last):
  File "F:\Games\Programming_Projects\Python\Zapzatron_Club\Retrieval_QA\local\test\ngrok_ssl_error\client.py", line 14, in <module>
    asyncio.run(main())
  File "F:\Techno\Languages\Python3109\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "F:\Techno\Languages\Python3109\lib\asyncio\base_events.py", line 649, in run_until_complete
    return future.result()
  File "F:\Games\Programming_Projects\Python\Zapzatron_Club\Retrieval_QA\local\test\ngrok_ssl_error\client.py", line 10, in main
    async with session.request("post", path, json={"some": "message"}) as response:
  File "F:\Games\Programming_Projects\Python\Zapzatron_Club\Retrieval_QA\.venv\lib\site-packages\aiohttp\client.py", line 1194, in __aenter__
    self._resp = await self._coro
  File "F:\Games\Programming_Projects\Python\Zapzatron_Club\Retrieval_QA\.venv\lib\site-packages\aiohttp\client.py", line 605, in _request
    await resp.start(conn)
  File "F:\Games\Programming_Projects\Python\Zapzatron_Club\Retrieval_QA\.venv\lib\site-packages\aiohttp\client_reqrep.py", line 966, in start
    message, payload = await protocol.read()  # type: ignore[union-attr]
  File "F:\Games\Programming_Projects\Python\Zapzatron_Club\Retrieval_QA\.venv\lib\site-packages\aiohttp\streams.py", line 622, in read
    await self._waiter
aiohttp.client_exceptions.ClientOSError: [Errno 1] [SSL: DECRYPTION_FAILED_OR_BAD_RECORD_MAC] decryption failed or bad record mac (_ssl.c:2548)

Add `labels` as a named parameter of `ngrok.forward` method

I was mentioned in this issue/comment that it's possible to use the labels keyword argument with the ngrok.forward method (e.g. ngrok.forward("localhost:8080", proto="labeled", labels="edge:edghts_2G...", authtoken_from_env=True)), which is great for specifying a specific edge for creating a tunnel.

It would be helpful to users if labels was an named parameter of the ngrok.forward method (rather than just supporting it as a keyword argument that works if you know it exists.

Thanks!

Can't start labeled tunnel

I cannot find any way to start ngrok agent from code with label to attach my agent to specific edge. Agent starts correctly and it is visible on agent's page. Is it possible to specify labels when connecting to ngrok?

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.