service

package
v3.1.0-alpha.4+incompa... Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2018 License: Apache-2.0 Imports: 58 Imported by: 0

Documentation

Overview

Package service implements teleport running service, takes care of initialization, cleanup and shutdown procedures

Copyright 2015 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Index

Constants

View Source
const (
	// AuthIdentityEvent is generated when the Auth Servers identity has been
	// initialized in the backend.
	AuthIdentityEvent = "AuthIdentity"

	// ProxyIdentityEvent is generated by the supervisor when the proxy's
	// identity has been registered with the Auth Server.
	ProxyIdentityEvent = "ProxyIdentity"

	// SSHIdentityEvent is generated when node's identity has been registered
	// with the Auth Server.
	SSHIdentityEvent = "SSHIdentity"

	// AuthTLSReady is generated when the Auth Server has initialized the
	// TLS Mutual Auth endpoint and is ready to start accepting connections.
	AuthTLSReady = "AuthTLSReady"

	// ProxyWebServerReady is generated when the proxy has initialized the web
	// server and is ready to start accepting connections.
	ProxyWebServerReady = "ProxyWebServerReady"

	// ProxyReverseTunnelReady is generated when the proxy has initialized the
	// reverse tunnel server and is ready to start accepting connections.
	ProxyReverseTunnelReady = "ProxyReverseTunnelReady"

	// ProxyAgentPoolReady is generated when the proxy has initialized the agent
	// pool (pool of connections from a remote cluster to a main cluster) and is
	// ready to start accepting connections.
	ProxyAgentPoolReady = "ProxyAgentPoolReady"

	// ProxySSHReady is generated when the proxy has initialized a SSH server
	// and is ready to start accepting connections.
	ProxySSHReady = "ProxySSHReady"

	// NodeReady is generated when the Teleport node has initialized a SSH server
	// and is ready to start accepting SSH connections.
	NodeSSHReady = "NodeReady"

	// TeleportExitEvent is generated when the Teleport process begins closing
	// all listening sockets and exiting.
	TeleportExitEvent = "TeleportExit"

	// TeleportReloadEvent is generated to trigger in-process teleport
	// service reload - all servers and clients will be re-created
	// in a graceful way.
	TeleportReloadEvent = "TeleportReload"

	// TeleportPhaseChangeEvent is generated to indidate that teleport
	// CA rotation phase has been updated, used in tests
	TeleportPhaseChangeEvent = "TeleportPhaseChange"

	// TeleportReadyEvent is generated to signal that all teleport
	// internal components have started successfully.
	TeleportReadyEvent = "TeleportReady"

	// ServiceExitedWithErrorEvent is emitted whenever a service
	// has exited with an error, the payload includes the error
	ServiceExitedWithErrorEvent = "ServiceExitedWithError"
)

Variables

View Source
var ErrTeleportExited = &trace.CompareFailedError{Message: "teleport process has shutdown"}

ErrTeleportExited means that teleport has exited

View Source
var ErrTeleportReloading = &trace.CompareFailedError{Message: "teleport process is reloading"}

ErrTeleportReloading is returned when signal waiter exits because the teleport process has initiaded shutdown

Functions

func ApplyDefaults added in v1.0.0

func ApplyDefaults(cfg *Config)

ApplyDefaults applies default values to the existing config structure

func Run

func Run(ctx context.Context, cfg Config, newTeleport NewProcess) error

Run starts teleport processes, waits for signals and handles internal process reloads.

Types

type AuthConfig

type AuthConfig struct {
	// Enabled turns auth role on or off for this process
	Enabled bool

	// EnableProxyProtocol enables proxy protocol support
	EnableProxyProtocol bool

	// SSHAddr is the listening address of SSH tunnel to HTTP service
	SSHAddr utils.NetAddr

	// Authorities is a set of trusted certificate authorities
	// that will be added by this auth server on the first start
	Authorities []services.CertAuthority

	// Roles is a set of roles to pre-provision for this cluster
	Roles []services.Role

	// ClusterName is a name that identifies this authority and all
	// host nodes in the cluster that will share this authority domain name
	// as a base name, e.g. if authority domain name is example.com,
	// all nodes in the cluster will have UUIDs in the form: <uuid>.example.com
	ClusterName services.ClusterName

	// StaticTokens are pre-defined host provisioning tokens supplied via config file for
	// environments where paranoid security is not needed
	StaticTokens services.StaticTokens

	// StorageConfig contains configuration settings for the storage backend.
	StorageConfig backend.Config

	Limiter limiter.LimiterConfig

	// NoAudit, when set to true, disables session recording and event audit
	NoAudit bool

	// Preference defines the authentication preference (type and second factor) for
	// the auth server.
	Preference services.AuthPreference

	// ClusterConfig stores cluster level configuration.
	ClusterConfig services.ClusterConfig

	// LicenseFile is a full path to the license file
	LicenseFile string

	// PublicAddrs affects the SSH host principals and DNS names added to the SSH and TLS certs.
	PublicAddrs []utils.NetAddr

	// KubeconfigPath is a path to kubeconfig
	KubeconfigPath string
}

AuthConfig is a configuration of the auth server

type CachePolicy

type CachePolicy struct {
	// Enabled enables or disables caching
	Enabled bool
	// TTL sets maximum TTL for the cached values
	// without explicit TTL set
	TTL time.Duration
	// NeverExpires means that cache values without TTL
	// set by the auth server won't expire
	NeverExpires bool
	// RecentTTL is the recently accessed items cache TTL
	RecentTTL *time.Duration
}

CachePolicy sets caching policy for proxies and nodes

func (*CachePolicy) GetRecentTTL

func (c *CachePolicy) GetRecentTTL() time.Duration

GetRecentTTL either returns TTL that was set, or default recent TTL value

func (CachePolicy) String

func (c CachePolicy) String() string

String returns human-friendly representation of the policy

type Config

type Config struct {
	// DataDir provides directory where teleport stores it's permanent state
	// (in case of auth server backed by BoltDB) or local state, e.g. keys
	DataDir string

	// Hostname is a node host name
	Hostname string

	// Token is used to register this Teleport instance with the auth server
	Token string

	// AuthServers is a list of auth servers nodes, proxies and peer auth servers
	// connect to
	AuthServers []utils.NetAddr

	// Identities is an optional list of pre-generated key pairs
	// for teleport roles, this is helpful when server is preconfigured
	Identities []*auth.Identity

	// AdvertiseIP is used to "publish" an alternative IP address or hostname this node
	// can be reached on, if running behind NAT
	AdvertiseIP string

	// CachePolicy sets caching policy for nodes and proxies
	// in case if they loose connection to auth servers
	CachePolicy CachePolicy

	// SSH role an SSH endpoint server
	SSH SSHConfig

	// Auth server authentication and authorization server config
	Auth AuthConfig

	// Keygen points to a key generator implementation
	Keygen sshca.Authority

	// Proxy is SSH proxy that manages incoming and outbound connections
	// via multiple reverse tunnels
	Proxy ProxyConfig

	// HostUUID is a unique UUID of this host (it will be known via this UUID within
	// a teleport cluster). It's automatically generated on 1st start
	HostUUID string

	// Console writer to speak to a user
	Console io.Writer

	// ReverseTunnels is a list of reverse tunnels to create on the
	// first cluster start
	ReverseTunnels []services.ReverseTunnel

	// OIDCConnectors is a list of trusted OpenID Connect identity providers
	OIDCConnectors []services.OIDCConnector

	// PidFile is a full path of the PID file for teleport daemon
	PIDFile string

	// Trust is a service that manages users and credentials
	Trust services.Trust

	// Presence service is a discovery and hearbeat tracker
	Presence services.Presence

	// Provisioner is a service that keeps track of provisioning tokens
	Provisioner services.Provisioner

	// Trust is a service that manages users and credentials
	Identity services.Identity

	// Access is a service that controls access
	Access services.Access

	// ClusterConfiguration is a service that provides cluster configuration
	ClusterConfiguration services.ClusterConfiguration

	// CipherSuites is a list of TLS ciphersuites that Teleport supports. If
	// omitted, a Teleport selected list of defaults will be used.
	CipherSuites []uint16

	// Ciphers is a list of SSH ciphers that the server supports. If omitted,
	// the defaults will be used.
	Ciphers []string

	// KEXAlgorithms is a list of SSH key exchange (KEX) algorithms that the
	// server supports. If omitted, the defaults will be used.
	KEXAlgorithms []string

	// MACAlgorithms is a list of SSH message authentication codes (MAC) that
	// the server supports. If omitted the defaults will be used.
	MACAlgorithms []string

	// DiagnosticAddr is an address for diagnostic and healthz endpoint service
	DiagnosticAddr utils.NetAddr

	// Debug sets debugging mode, results in diagnostic address
	// endpoint extended with additional /debug handlers
	Debug bool

	// UploadEventsC is a channel for upload events
	// used in tests
	UploadEventsC chan *events.UploadEvent `json:"-"`

	// FileDescriptors is an optional list of file descriptors for the process
	// to inherit and use for listeners, used for in-process updates.
	FileDescriptors []FileDescriptor

	// PollingPeriod is set to override default internal polling periods
	// of sync agents, used to speed up integration tests.
	PollingPeriod time.Duration

	// ClientTimeout is set to override default client timeouts
	// used by internal clients, used to speed up integration tests.
	ClientTimeout time.Duration

	// ShutdownTimeout is set to override default shutdown timeout.
	ShutdownTimeout time.Duration

	// CAPin is the SKPI hash of the CA used to verify the Auth Server.
	CAPin string
}

Config structure is used to initialize _all_ services Teleporot can run. Some settings are global (like DataDir) while others are grouped into sections, like AuthConfig

func MakeDefaultConfig added in v1.0.0

func MakeDefaultConfig() (config *Config)

MakeDefaultConfig creates a new Config structure and populates it with defaults

func (*Config) ApplyToken added in v1.0.0

func (cfg *Config) ApplyToken(token string) bool

ApplyToken assigns a given token to all internal services but only if token is not an empty string.

Returns 'true' if token was modified

func (*Config) DebugDumpToYAML added in v1.0.0

func (cfg *Config) DebugDumpToYAML() string

DebugDumpToYAML is useful for debugging: it dumps the Config structure into a string

func (*Config) RoleConfig

func (cfg *Config) RoleConfig() RoleConfig

RoleConfig is a config for particular Teleport role

type Connector added in v1.0.0

type Connector struct {
	// ClientIdentity is the identity to be used in internal cluster
	// clients to the auth service.
	ClientIdentity *auth.Identity
	// ServerIdentity is the identity to be used in servers - serving SSH
	// and x509 certificates to clients.
	ServerIdentity *auth.Identity
	// Client is authenticated client with credentials from ClientIdenity.
	Client *auth.Client
}

Connector has all resources process needs to connect to other parts of the cluster: client and identity

type Event added in v1.0.0

type Event struct {
	Name    string
	Payload interface{}
}

Event is a special service event that can be generated by various goroutines in the supervisor

func (*Event) String added in v1.0.0

func (e *Event) String() string

type EventMapping

type EventMapping struct {
	// In is the incoming event sequence.
	In []string
	// Out is the outbound event to generate.
	Out string
}

EventMapping maps a sequence of incoming events and if triggered, generates an out event.

func (EventMapping) String

func (e EventMapping) String() string

String returns user-friendly representation of the mapping.

type FileDescriptor

type FileDescriptor struct {
	// Type is a listener type, e.g. auth:ssh
	Type string
	// Address is an addresss of the listener, e.g. 127.0.0.1:3025
	Address string
	// File is a file descriptor associated with the listener
	File *os.File
}

FileDescriptor is a file descriptor associated with a listener

func (*FileDescriptor) ToListener

func (fd *FileDescriptor) ToListener() (net.Listener, error)

type KeyPair

type KeyPair struct {
	// PrivateKey is a private key in PEM format
	PrivateKey []byte
	// PublicSSHKey is a public key in SSH format
	PublicSSHKey []byte
	// PublicTLSKey is a public key in X509 format
	PublicTLSKey []byte
}

KeyPair is a private/public key pair

type KubeProxyConfig

type KubeProxyConfig struct {
	// Enabled turns kubernetes proxy role on or off for this process
	Enabled bool

	// ListenAddr is address where reverse tunnel dialers connect to
	ListenAddr utils.NetAddr

	// KubeAPIAddr is address of kubernetes API server
	APIAddr utils.NetAddr

	// ClusterOverride causes all traffic to go to a specific remote
	// cluster, used only in tests
	ClusterOverride string

	// CACert is a PEM encoded kubernetes CA certificate
	CACert []byte

	// PublicAddrs is a list of the public addresses the Teleport Kube proxy can be accessed by,
	// it also affects the host principals and routing logic
	PublicAddrs []utils.NetAddr
}

KubeProxyConfig specifies configuration for proxy service

type LocalService

type LocalService struct {
	// Function is a function to call
	Function ServiceFunc
	// ServiceName is a service name
	ServiceName string
	// Critical is set to true
	// when the service is critical and program can't continue
	// without it
	Critical bool
}

LocalService is a locally defined service

func (*LocalService) IsCritical

func (l *LocalService) IsCritical() bool

IsCritical returns true if the service is critical and program can't continue without it

func (*LocalService) Name

func (l *LocalService) Name() string

Name returns unique service name

func (*LocalService) Serve

func (l *LocalService) Serve() error

Serve starts the function

func (*LocalService) String

func (l *LocalService) String() string

String returns user-friendly service name

type LocalSupervisor

type LocalSupervisor struct {
	sync.Mutex
	// contains filtered or unexported fields
}

LocalSupervisor is a Teleport's implementation of the Supervisor interface.

func (*LocalSupervisor) BroadcastEvent added in v1.0.0

func (s *LocalSupervisor) BroadcastEvent(event Event)

BroadcastEvent generates event and broadcasts it to all subscribed parties.

func (*LocalSupervisor) ExitContext

func (s *LocalSupervisor) ExitContext() context.Context

ExitContext returns context that will be closed when TeleportExitEvent is broadcasted.

func (*LocalSupervisor) Register

func (s *LocalSupervisor) Register(srv Service)

func (*LocalSupervisor) RegisterCriticalFunc

func (s *LocalSupervisor) RegisterCriticalFunc(name string, fn ServiceFunc)

RegisterCriticalFunc creates a critical service from function spec and registers it within the system, if this service exits with error, the process shuts down.

func (*LocalSupervisor) RegisterEventMapping

func (s *LocalSupervisor) RegisterEventMapping(m EventMapping)

RegisterEventMapping registers event mapping - when the sequence in the event mapping triggers, the outbound event will be generated.

func (*LocalSupervisor) RegisterFunc

func (s *LocalSupervisor) RegisterFunc(name string, fn ServiceFunc)

RegisterFunc creates a service from function spec and registers it within the system

func (*LocalSupervisor) ReloadContext

func (s *LocalSupervisor) ReloadContext() context.Context

ReloadContext returns context that will be closed when TeleportReloadEvent is broadcasted.

func (*LocalSupervisor) RemoveService

func (s *LocalSupervisor) RemoveService(srv Service) error

RemoveService removes service from supervisor tracking list

func (*LocalSupervisor) Run

func (s *LocalSupervisor) Run() error

func (*LocalSupervisor) ServiceCount added in v1.0.0

func (s *LocalSupervisor) ServiceCount() int

ServiceCount returns the number of registered and actively running services

func (*LocalSupervisor) Services

func (s *LocalSupervisor) Services() []string

func (*LocalSupervisor) Start

func (s *LocalSupervisor) Start() error

func (*LocalSupervisor) Wait

func (s *LocalSupervisor) Wait() error

func (*LocalSupervisor) WaitForEvent added in v1.0.0

func (s *LocalSupervisor) WaitForEvent(ctx context.Context, name string, eventC chan Event)

WaitForEvent waits for event to be broadcasted, if the event was already broadcasted, eventC will receive current event immediately.

type NewProcess

type NewProcess func(cfg *Config) (Process, error)

NewProcess is a function that creates new teleport from config

type Process

type Process interface {
	// Closer closes all resources used by the process
	io.Closer
	// Start starts the process in a non-blocking way
	Start() error
	// WaitForSignals waits for and handles system process signals.
	WaitForSignals(context.Context) error
	// ExportFileDescriptors exports service listeners
	// file descriptors used by the process.
	ExportFileDescriptors() ([]FileDescriptor, error)
	// Shutdown starts graceful shutdown of the process,
	// blocks until all resources are freed and go-routines are
	// shut down.
	Shutdown(context.Context)
	// WaitForEvent waits for event to occur, sends event to the channel,
	// this is a non-blocking function.
	WaitForEvent(ctx context.Context, name string, eventC chan Event)
	// WaitWithContext waits for the service to stop. This is a blocking
	// function.
	WaitWithContext(ctx context.Context)
}

Process is a interface for processes

type ProxyConfig

type ProxyConfig struct {
	// Enabled turns proxy role on or off for this process
	Enabled bool

	//DisableTLS is enabled if we don't want self signed certs
	DisableTLS bool

	// DisableWebInterface allows to turn off serving the Web UI interface
	DisableWebInterface bool

	// DisableWebService turnes off serving web service completely, including web UI
	DisableWebService bool

	// DisableReverseTunnel disables reverse tunnel on the proxy
	DisableReverseTunnel bool

	// ReverseTunnelListenAddr is address where reverse tunnel dialers connect to
	ReverseTunnelListenAddr utils.NetAddr

	// EnableProxyProtocol enables proxy protocol support
	EnableProxyProtocol bool

	// WebAddr is address for web portal of the proxy
	WebAddr utils.NetAddr

	// SSHAddr is address of ssh proxy
	SSHAddr utils.NetAddr

	// TLSKey is a base64 encoded private key used by web portal
	TLSKey string

	// TLSCert is a base64 encoded certificate used by web portal
	TLSCert string

	Limiter limiter.LimiterConfig

	// PublicAddrs is a list of the public addresses the proxy advertises
	// for the HTTP endpoint. The hosts in in PublicAddr are included in the
	// list of host principals on the TLS and SSH certificate.
	PublicAddrs []utils.NetAddr

	// SSHPublicAddrs is a list of the public addresses the proxy advertises
	// for the SSH endpoint. The hosts in in PublicAddr are included in the
	// list of host principals on the TLS and SSH certificate.
	SSHPublicAddrs []utils.NetAddr

	// Kube specifies kubernetes proxy configuration
	Kube KubeProxyConfig
}

ProxyConfig specifies configuration for proxy service

type RegisteredListener

type RegisteredListener struct {
	// Type is a listener type, e.g. auth:ssh
	Type string
	// Address is an address listener is serving on, e.g. 127.0.0.1:3025
	Address string
	// Listener is a file listener object
	Listener net.Listener
}

RegisteredListener is a listener registered within teleport process, can be passed to child process

type RoleConfig

type RoleConfig struct {
	DataDir     string
	HostUUID    string
	HostName    string
	AuthServers []utils.NetAddr
	Auth        AuthConfig
	Console     io.Writer
}

RoleConfig is a configuration for a server role (either proxy or node)

type SSHConfig

type SSHConfig struct {
	Enabled               bool
	Addr                  utils.NetAddr
	Namespace             string
	Shell                 string
	Limiter               limiter.LimiterConfig
	Labels                map[string]string
	CmdLabels             services.CommandLabels
	PermitUserEnvironment bool

	// PAM holds PAM configuration for Teleport.
	PAM *pam.Config

	// PublicAddrs affects the SSH host principals and DNS names added to the SSH and TLS certs.
	PublicAddrs []utils.NetAddr
}

SSHConfig configures SSH server node role

type Service

type Service interface {
	// Serve starts the function
	Serve() error
	// String returns user-friendly description of service
	String() string
	// Name returns service name
	Name() string
	// IsCritical returns true if the service is critical
	// and program can't continue without it
	IsCritical() bool
}

Service is a running teleport service function

type ServiceExit

type ServiceExit struct {
	// Service is the service that exited
	Service Service
	// Error is the error of the service exit
	Error error
}

ServiceExit contains information about service name, and service error if it exited with error

type ServiceFunc

type ServiceFunc func() error

ServiceFunc is a service function

type Supervisor

type Supervisor interface {
	// Register adds the service to the pool, if supervisor is in
	// the started state, the service will be started immediately
	// otherwise, it will be started after Start() has been called
	Register(srv Service)

	// RegisterFunc creates a service from function spec and registers
	// it within the system
	RegisterFunc(name string, fn ServiceFunc)

	// RegisterCriticalFunc creates a critical service from function spec and registers
	// it within the system, if this service exits with error,
	// the process shuts down.
	RegisterCriticalFunc(name string, fn ServiceFunc)

	// ServiceCount returns the number of registered and actively running
	// services
	ServiceCount() int

	// Start starts all unstarted services
	Start() error

	// Wait waits until all services exit
	Wait() error

	// Run starts and waits for the service to complete
	// it's a combinatioin Start() and Wait()
	Run() error

	// Services returns list of running services
	Services() []string

	// BroadcastEvent generates event and broadcasts it to all
	// subscribed parties.
	BroadcastEvent(Event)

	// WaitForEvent waits for event to be broadcasted, if the event
	// was already broadcasted, eventC will receive current event immediately.
	WaitForEvent(ctx context.Context, name string, eventC chan Event)

	// RegisterEventMapping registers event mapping -
	// when the sequence in the event mapping triggers, the
	// outbound event will be generated.
	RegisterEventMapping(EventMapping)

	// ExitContext returns context that will be closed when
	// TeleportExitEvent is broadcasted.
	ExitContext() context.Context

	// ReloadContext returns context that will be closed when
	// TeleportReloadEvent is broadcasted.
	ReloadContext() context.Context
}

Supervisor implements the simple service logic - registering service functions and de-registering the service goroutines

func NewSupervisor

func NewSupervisor(id string) Supervisor

NewSupervisor returns new instance of initialized supervisor

type TeleportProcess added in v1.0.0

type TeleportProcess struct {
	clockwork.Clock
	sync.Mutex
	Supervisor
	Config *Config

	// identities of this process (credentials to auth sever, basically)
	Identities map[teleport.Role]*auth.Identity

	// Entry is a process-local log entry.
	*logrus.Entry
	// contains filtered or unexported fields
}

TeleportProcess structure holds the state of the Teleport daemon, controlling execution and configuration of the teleport services: ssh, auth and proxy.

func NewTeleport

func NewTeleport(cfg *Config) (*TeleportProcess, error)

NewTeleport takes the daemon configuration, instantiates all required services and starts them under a supervisor, returning the supervisor object.

func (*TeleportProcess) Close added in v1.0.0

func (process *TeleportProcess) Close() error

Close broadcasts close signals and exits immediately

func (*TeleportProcess) ExportFileDescriptors

func (process *TeleportProcess) ExportFileDescriptors() ([]FileDescriptor, error)

ExportFileDescriptors exports file descriptors to be passed to child process

func (*TeleportProcess) GetAuditLog

func (process *TeleportProcess) GetAuditLog() events.IAuditLog

GetAuditLog returns the process' audit log

func (*TeleportProcess) GetAuthServer added in v1.0.0

func (process *TeleportProcess) GetAuthServer() *auth.AuthServer

GetAuthServer returns the process' auth server

func (*TeleportProcess) GetBackend

func (process *TeleportProcess) GetBackend() backend.Backend

GetBackend returns the process' backend

func (*TeleportProcess) GetIdentity added in v1.2.6

func (process *TeleportProcess) GetIdentity(role teleport.Role) (i *auth.Identity, err error)

GetIdentity returns the process identity (credentials to the auth server) for a given teleport Role. A teleport process can have any combination of 3 roles: auth, node, proxy and they have their own identities

func (*TeleportProcess) Shutdown

func (process *TeleportProcess) Shutdown(ctx context.Context)

Shutdown launches graceful shutdown process and waits for it to complete

func (*TeleportProcess) StartShutdown

func (process *TeleportProcess) StartShutdown(ctx context.Context) context.Context

StartShutdown launches non-blocking graceful shutdown process that signals completion, returns context that will be closed once the shutdown is done

func (*TeleportProcess) WaitForSignals

func (process *TeleportProcess) WaitForSignals(ctx context.Context) error

WaitForSignals waits for system signals and processes them. Should not be called twice by the process.

func (*TeleportProcess) WaitWithContext

func (process *TeleportProcess) WaitWithContext(ctx context.Context)

WaitWithContext waits until all internal services stop.

Jump to

Keyboard shortcuts

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