gate

package
v0.6.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2024 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

The client library for registering paths with ask.systems/daemon/portal.

This package contains the raw gRPC service protos in addition to helper functions for using the service. The helpers are:

  • StartTLSRegistration, MustStartTLSRegistration, StartRegistration, and MustStartRegistration are all in one helpers for getting a lease from portal and keeping it renewed in the background. Most clients should use one of these.
  • ParsePattern which splits out the hostname part of multi-hostname patterns used to register with portal when portal is hosting multiple URLs. This is needed to extract the path part that can be used with [http.Handle].
  • Client and the methods under it provide full access to the gRPC server using the token authentication. Call methods directly using Client.RPC if you want detailed access to portal.

You need to set the Address and Token vars to use the 4 helper functions like StartTLSRegistration so it can connect to portal. The simplest way to do this is to import the ask.systems/daemon/portal/flags library:

import (
	_ "ask.systems/daemon/portal/flags"
)

When running portal client binaries on the commandline you can use the PORTAL_ADDR and PORTAL_TOKEN environment variables to easily set the values.

Example
package main

import (
	"flag"
	"log"
	"net/http"

	_ "ask.systems/daemon/portal/flags" // -portal_addr and -portal_token
	_ "ask.systems/daemon/tools/flags"  // for the -version and -syslog flags

	"ask.systems/daemon/portal/gate"
	"ask.systems/daemon/tools"
)

var pattern = flag.String("pattern", "/hello/", "The path to register with portal")

func main() {
	flag.Parse()
	quit := make(chan struct{})
	tools.CloseOnQuitSignals(quit) // close the channel when the OS says to stop

	// Remove the optional URL prefix from the pattern, portal can serve multiple URLs
	_, path := gate.ParsePattern(*pattern)
	// Register with portal, generate a TLS cert signed by portal, and keep the
	// registration and cert renewed in the background (until quit is closed)
	lease, tlsConf := gate.MustStartTLSRegistration(&gate.RegisterRequest{
		Pattern: *pattern,
	}, quit)

	// Serve Hello World with standard go server functions
	http.Handle(path, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte("Hello World!"))
		// portal adds this header to tell you who sent the request to portal
		log.Printf("Hello from %v:%v",
			req.Header.Get("X-Forwarded-For"),
			req.Header.Get("X-Forwarded-For-Port"))
	}))

	// Run the server and block until the channel is closed and the graceful stop
	// is done. Uses the handlers registered with the http package global system
	tools.RunHTTPServerTLS(lease.Port, tlsConf, quit)
	log.Print("Goodbye")
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// The hostname (or IP) and port of the portal server to connect to.
	Address *string
	// The API authentication token for portal RPCs. Portal logs this on startup.
	Token *string
)

Configuration for connecting to portal

If you read these vars directly, call ResolveFlags first!

These are set by the ask.systems/daemon/portal/flags library. If you don't want to use the flags you can set the values here.

View Source
var File_gate_service_proto protoreflect.FileDescriptor
View Source
var (
	NotRegisteredError = errors.New("pattern not registered")
)
View Source
var Portal_ServiceDesc = grpc.ServiceDesc{
	ServiceName: "Portal",
	HandlerType: (*PortalServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "Register",
			Handler:    _Portal_Register_Handler,
		},
		{
			MethodName: "Renew",
			Handler:    _Portal_Renew_Handler,
		},
		{
			MethodName: "Unregister",
			Handler:    _Portal_Unregister_Handler,
		},
		{
			MethodName: "MyHostname",
			Handler:    _Portal_MyHostname_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "gate/service.proto",
}

Portal_ServiceDesc is the grpc.ServiceDesc for Portal service. It's only intended for direct use with grpc.RegisterService, and not to be introspected or modified (even as a copy)

View Source
var ServiceProto string

The text of the service.proto file so that clients can print the schema.

Functions

func ParsePattern

func ParsePattern(pattern string) (host, path string)

Parse a pattern in the syntax accepted by portal separating the hostname (URL) part of the pattern from the path part. The path part is then compatible with net/http.Handle

This is needed to host multiple URLs with portal.

func RegisterPortalServer

func RegisterPortalServer(s grpc.ServiceRegistrar, srv PortalServer)

func ResolveFlags

func ResolveFlags() error

Sets up Address and Token based on optional ask.systems/daemon/portal/flags values and with fallback to using the PORTAL_ADDR and PORTAL_TOKEN environment variable values if the the variables were not already set in code or by the flags.

Example
package main

import (
	"log"

	"ask.systems/daemon/portal/gate"
)

func main() {
	if err := gate.ResolveFlags(); err != nil {
		log.Fatal(err)
	}
	c, err := gate.Connect(*gate.Address, *gate.Token)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()
	log.Print("Connected to portal!")
}
Output:

Types

type Client

type Client struct {
	// Call any of the service.proto functions here
	RPC PortalClient
	// contains filtered or unexported fields
}
Example
package main

import (
	"context"
	"log"

	"ask.systems/daemon/portal/gate"
	"google.golang.org/protobuf/types/known/emptypb"
)

func main() {
	// This call sets the globals from flags and environment variables.
	// import _ "ask.systems/daemon/portal/flags" to get the flag definitions.
	if err := gate.ResolveFlags(); err != nil {
		log.Fatal(err)
	}
	client, err := gate.Connect(*gate.Address, *gate.Token)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()
	// Call the MyHostname RPC directly. This is used by [StartTLSRegistration] to
	// set the hostname in the TLS certificate request.
	resp, err := client.RPC.MyHostname(context.Background(), &emptypb.Empty{})
	if err != nil {
		log.Fatal(err)
	}
	log.Print(resp.Hostname)
}
Output:

func Connect

func Connect(portalAddr, token string) (Client, error)

Connect to the portal RPC server and don't do anything else. Use this if you want to call the proto RPCs directly.

func (Client) Close

func (c Client) Close()

Close the connection to the RPC server

func (Client) KeepLeaseRenewed deprecated

func (c Client) KeepLeaseRenewed(quit <-chan struct{}, lease *Lease)

Run a loop to call the Renew RPC for the given lease before the lease expires until the quit channel is closed. Intended to be run in a goroutine.

When the quit channel is closed this function unregisters the lease and closes the client.

Deprecated: It's best to use StartRegistration because this function cannot re-register the lease in the unlikely event that the server somehow forgets the lease exists when it tries to renew, because it does not have access to the registration request. In that case this function gets stuck trying to renew forever. The server can't do it because it has forgetten in this scenario.

func (Client) KeepLeaseRenewedTLS deprecated

func (c Client) KeepLeaseRenewedTLS(quit <-chan struct{}, lease *Lease, newCert func([]byte))

Run a loop to call the Renew RPC for the given lease before the lease expires until the quit channel is closed. Intended to be run in a goroutine.

Additionally, if the lease contains a TLS certificate request, the certificate is renewed with the lease. Each time the certificate is renewed, the newCert function is called with the cert data.

When the quit channel is closed this function unregisters the lease and closes the client.

Deprecated: It's best to use StartTLSRegistration because this function cannot re-register the lease in the unlikely event that the server somehow forgets the lease exists when it tries to renew, because it does not have access to the registration request. In that case this function gets stuck trying to renew forever. The server can't do it because it has forgetten in this scenario.

type Hostname

type Hostname struct {
	Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
	// contains filtered or unexported fields
}

func (*Hostname) Descriptor deprecated

func (*Hostname) Descriptor() ([]byte, []int)

Deprecated: Use Hostname.ProtoReflect.Descriptor instead.

func (*Hostname) GetHostname

func (x *Hostname) GetHostname() string

func (*Hostname) ProtoMessage

func (*Hostname) ProtoMessage()

func (*Hostname) ProtoReflect

func (x *Hostname) ProtoReflect() protoreflect.Message

func (*Hostname) Reset

func (x *Hostname) Reset()

func (*Hostname) String

func (x *Hostname) String() string

type Lease

type Lease struct {
	Pattern string                 `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"`
	Address string                 `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"`
	Port    uint32                 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
	Timeout *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"`
	// If generate_certificate was set in the request, this is the signed x509
	// certificate to use for your server. It will be renewed with the lease.
	Certificate []byte `protobuf:"bytes,4,opt,name=Certificate,proto3" json:"Certificate,omitempty"`
	// contains filtered or unexported fields
}

func MustStartRegistration

func MustStartRegistration(request *RegisterRequest, quit <-chan struct{}) *Lease

The same as StartRegistration but call log.Fatal on error

func MustStartTLSRegistration

func MustStartTLSRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, *tls.Config)

The same as StartTLSRegistration but call log.Fatal on error

func StartRegistration

func StartRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, error)

Make a connection to the portal RPC service and send the registration request. Also starts a goroutine to renew (and potentially re-register) the lease until the quit channel is closed.

Returns the initial lease or an error if the registration didn't work.

func StartTLSRegistration

func StartTLSRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, *tls.Config, error)

Make a connection to the portal RPC service and send the registration request. Additionally, generate a new TLS certificate and request portal to sign it with portal's private Certificate Authority (the key is never sent over the network).

Starts a goroutine to renew the both the lease and the TLS certificate (and re-register the lease if necessary) until the quit channel is closed.

Returns the initial lease, and a tls.Config which automatically renews the certificate seemlessly. Or return an error if the registration didn't work.

func (*Lease) Descriptor deprecated

func (*Lease) Descriptor() ([]byte, []int)

Deprecated: Use Lease.ProtoReflect.Descriptor instead.

func (*Lease) GetAddress added in v0.6.0

func (x *Lease) GetAddress() string

func (*Lease) GetCertificate

func (x *Lease) GetCertificate() []byte

func (*Lease) GetPattern

func (x *Lease) GetPattern() string

func (*Lease) GetPort

func (x *Lease) GetPort() uint32

func (*Lease) GetTimeout

func (x *Lease) GetTimeout() *timestamppb.Timestamp

func (*Lease) ProtoMessage

func (*Lease) ProtoMessage()

func (*Lease) ProtoReflect

func (x *Lease) ProtoReflect() protoreflect.Message

func (*Lease) Reset

func (x *Lease) Reset()

func (*Lease) String

func (x *Lease) String() string

type PortalClient

type PortalClient interface {
	Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*Lease, error)
	Renew(ctx context.Context, in *Lease, opts ...grpc.CallOption) (*Lease, error)
	Unregister(ctx context.Context, in *Lease, opts ...grpc.CallOption) (*Lease, error)
	// Returns the address that will be used to connect to your server if
	// registered. It is necessary to register the correct hostname in the TLS
	// certificate signed by portal.
	MyHostname(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Hostname, error)
}

PortalClient is the client API for Portal service.

For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.

func NewPortalClient

func NewPortalClient(cc grpc.ClientConnInterface) PortalClient

type PortalServer

type PortalServer interface {
	Register(context.Context, *RegisterRequest) (*Lease, error)
	Renew(context.Context, *Lease) (*Lease, error)
	Unregister(context.Context, *Lease) (*Lease, error)
	// Returns the address that will be used to connect to your server if
	// registered. It is necessary to register the correct hostname in the TLS
	// certificate signed by portal.
	MyHostname(context.Context, *emptypb.Empty) (*Hostname, error)
	// contains filtered or unexported methods
}

PortalServer is the server API for Portal service. All implementations must embed UnimplementedPortalServer for forward compatibility

type RegisterRequest

type RegisterRequest struct {

	// For HTTP: A url pattern that works with http.DefaultServMux. Ex: /images/
	// For TCP: ":tcp:port" for the port number portal should listen on. Only tcp
	// is accepted for now.
	//
	// HTTP patterns optionally accept a hostname (URL) constraint prefix. Or if
	// portal is configured to use the default hostname for no hostname patterns,
	// you can use * for the hostname to always match all URLs. For example:
	//
	//	ask.systems/images/
	//	*/favicon.ico
	Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` // TODO: maybe support multiple patterns for the same IP/port
	// Set for third party web interfaces (or TCP proxy backends) that can't
	// use an random lease port.
	// Must be outside the range of portal's automatic ports.
	FixedPort uint32 `protobuf:"varint,2,opt,name=fixed_port,json=fixedPort,proto3" json:"fixed_port,omitempty"`
	// Optional: If set, forward the requests for pattern to this IP/hostname.
	// If unset, forward requests to the IP that sent the RegisterRequest.
	//
	// It is easiest to just run assimilate on the machine you want the forwarding
	// rule for, but if you have a cpanel-only host or otherwise don't have access
	// to run arbitrary code then you can use this setting to run assimilate on
	// another machine.
	//
	// If you use this you need to be mindful of TLS and the network you're
	// sending the data over. Ideally you should set up a self-signed certificate
	// on the other machine and portal will detect TLS support. Otherwise make
	// sure you only use this with a trusted local network.
	Hostname string `protobuf:"bytes,6,opt,name=hostname,proto3" json:"hostname,omitempty"`
	// If true, remove the pattern in the URL of HTTP requests we forward to the
	// backend to hide that it is behind a reverse proxy.
	//
	// For example: If the pattern is /foo/ and the request is for /foo/index.html
	//   - If true  the request to the backend is /index.html
	//   - If false the request to the backend is /foo/index.html
	//
	// Ignored for TCP proxies.
	StripPattern bool `protobuf:"varint,3,opt,name=strip_pattern,json=stripPattern,proto3" json:"strip_pattern,omitempty"`
	// If true, do not redirect HTTP requests to HTTPS. This means the data will
	// be sent in plain-text readable to anyone if the client requests it. Some
	// legacy systems require plain HTTP requests. Leave this off by default for
	// security, that way responses will only be readable by the client.
	//
	// Ignored for TCP proxies.
	AllowHttp bool `protobuf:"varint,5,opt,name=allow_http,json=allowHttp,proto3" json:"allow_http,omitempty"`
	// If set, the server will sign the certificate request with portal's
	// certificate as the root and accept connections to the signed cert. This way
	// network traffic behind the reverse proxy can be encrypted.
	CertificateRequest []byte `protobuf:"bytes,4,opt,name=certificate_request,json=certificateRequest,proto3" json:"certificate_request,omitempty"`
	// contains filtered or unexported fields
}

func (*RegisterRequest) Descriptor deprecated

func (*RegisterRequest) Descriptor() ([]byte, []int)

Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead.

func (*RegisterRequest) GetAllowHttp added in v0.5.1

func (x *RegisterRequest) GetAllowHttp() bool

func (*RegisterRequest) GetCertificateRequest

func (x *RegisterRequest) GetCertificateRequest() []byte

func (*RegisterRequest) GetFixedPort

func (x *RegisterRequest) GetFixedPort() uint32

func (*RegisterRequest) GetHostname added in v0.6.0

func (x *RegisterRequest) GetHostname() string

func (*RegisterRequest) GetPattern

func (x *RegisterRequest) GetPattern() string

func (*RegisterRequest) GetStripPattern

func (x *RegisterRequest) GetStripPattern() bool

func (*RegisterRequest) ProtoMessage

func (*RegisterRequest) ProtoMessage()

func (*RegisterRequest) ProtoReflect

func (x *RegisterRequest) ProtoReflect() protoreflect.Message

func (*RegisterRequest) Reset

func (x *RegisterRequest) Reset()

func (*RegisterRequest) String

func (x *RegisterRequest) String() string

type UnimplementedPortalServer

type UnimplementedPortalServer struct {
}

UnimplementedPortalServer must be embedded to have forward compatible implementations.

func (UnimplementedPortalServer) MyHostname

func (UnimplementedPortalServer) Register

func (UnimplementedPortalServer) Renew

func (UnimplementedPortalServer) Unregister

type UnsafePortalServer

type UnsafePortalServer interface {
	// contains filtered or unexported methods
}

UnsafePortalServer may be embedded to opt out of forward compatibility for this service. Use of this interface is not recommended, as added methods to PortalServer will result in compilation errors.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL