Commit 7ca49e43 authored by Daniel P. Berrangé's avatar Daniel P. Berrangé 💬
Browse files

Initial commit


Signed-off-by: Daniel P. Berrangé's avatarDaniel P. Berrange <berrange@redhat.com>
parents
*~
vendor/
libvirt-console-proxy
glide.lock
The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
\ No newline at end of file
CMDS := libvirt-console-proxy
GOSRC := $(wildcard cmd/*/*.go) $(wildcard consoleproxy/*.go)
all: $(CMDS)
glide.lock: glide.yaml
glide install
libvirt-console-proxy: cmd/libvirt-console-proxy/libvirt-console-proxy.go $(GOSRC) glide.lock
go build -o $@ $<
clean:
rm -f $(CMDS)
# libvirt-console-proxy [![Build Status](https://travis-ci.org/libvirt/libvirt-console-proxy.svg?branch=master)](https://travis-ci.org/libvirt/libvirt-console-proxy) [![GoDoc](https://godoc.org/github.com/libvirt/libvirt-console-proxy?status.svg)](https://godoc.org/github.com/libvirt/libvirt-console-proxy)
Websockets console proxy for VNC, SPICE and serial consoles
This package provides a general purpose websockets proxy frontend for VNC,
SPICE and serial console servers.
## Building
This project uses go vendoring for 3rd party deps and does not keep the
deps in git. The 'glide' tool must be used to populate the vendor/
directory with the 3rd party modules. If not already available via your
OS distribution packages, install glide from:
* https://github.com/Masterminds/glide
and then run 'glide install' to populate vendor/
## Contributing
Bug fixes and other improvements to the libvirt-console-proxy are
welcome at any time. The preferred submission method is to use
git send-email to submit patches to the libvir-list@redhat.com
mailing list. eg. to send a single patch
```
# git send-email --to libvir-list@redhat.com --subject-prefix "PATCH console-proxy" \
--smtp-server=$HOSTNAME -1
```
Or to send all patches on the current branch, against master
```
$ git send-email --to libvir-list@redhat.com --subject-prefix "PATCH console-proxy" \
--smtp-server=$HOSTNAME --no-chain-reply-to --cover-letter --annotate \
master..
```
Note the master GIT repository is at
```
http://libvirt.org/git/?p=libvirt-console-proxy.git;a=summary
```
The following automatic read-only mirrors are available as a
convenience to allow contributors to "fork" the repository:
```
https://gitlab.com/libvirt/libvirt-console-proxy
https://github.com/libvirt/libvirt-console-proxy
```
While you can send pull-requests to these mirrors, they will be
re-submitted via emai to the mailing list for review before
being merged, unless they are trivial/obvious bug fixes.
/*
* This file is part of the libvirt-console-proxy project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Copyright (C) 2016 Red Hat, Inc.
*
*/
package main
import (
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"github.com/golang/glog"
"io/ioutil"
proxy "libvirt.org/libvirt-console-proxy/consoleproxy"
"os"
"strings"
)
var (
listeninsecure = flag.Bool("listen-insecure", false,
"Run public listener without TLS encryption")
listenaddr = flag.String("listen-addr", "0.0.0.0:80",
"TCP address and port to listen on")
listentlscert = flag.String("listen-tls-cert", "/etc/pki/libvirt-console-proxy/server-cert.pem",
"Path to TLS public server cert PEM file")
listentlskey = flag.String("listen-tls-key", "/etc/pki/libvirt-console-proxy/server-key.pem",
"Path to TLS public server key PEM file")
listentlsca = flag.String("listen-tls-ca", "/etc/pki/libvirt-console-proxy/server-ca.pem",
"Path to TLS public server CA cert PEM file")
connectortype = flag.String("connector-type", "fixed",
"Connector to use to access compute node servers")
connectinsecure = flag.Bool("connect-insecure", false,
"Run internal connection without TLS encryption")
connectaddr = flag.String("connect-addr", "127.0.0.1:5900",
"TCP address and port to connect to")
connectservice = flag.String("connect-service", "vnc",
"Service type to connect to (vnc, spice or serial)")
connecttlscert = flag.String("connect-tls-cert", "/etc/pki/libvirt-console-proxy/client-cert.pem",
"Path to TLS internal client cert PEM file")
connecttlskey = flag.String("connect-tls-key", "/etc/pki/libvirt-console-proxy/client-key.pem",
"Path to TLS internal client key PEM file")
connecttlsca = flag.String("connect-tls-ca", "/etc/pki/libvirt-console-proxy/client-ca.pem",
"Path to TLS internal client CA PEM file")
)
func loadTLSConfig(certFile, keyFile, caFile, addr string) (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
ca, err := ioutil.ReadFile(caFile)
if err != nil {
return nil, err
}
calist := x509.NewCertPool()
ok := calist.AppendCertsFromPEM(ca)
if !ok {
return nil, fmt.Errorf("Error loading CA certs from %s", caFile)
}
var config *tls.Config
if addr != "" {
addrbits := strings.Split(addr, ":")
if len(addrbits) != 2 {
return nil, fmt.Errorf("Expected host:port in %s\n", addr)
}
config = &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: calist,
ServerName: addrbits[0],
}
} else {
config = &tls.Config{
Certificates: []tls.Certificate{cert},
ClientCAs: calist,
}
}
return config, nil
}
func main() {
flag.Parse()
var connector proxy.Connector
switch proxy.ConnectorType(*connectortype) {
case proxy.CONNECTOR_FIXED:
var connecttlsconfig *tls.Config
if !*connectinsecure {
var err error
connecttlsconfig, err = loadTLSConfig(*connecttlscert, *connecttlskey, *connecttlsca, *connectaddr)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
svcconfig := &proxy.ServiceConfig{
Type: proxy.ServiceType(*connectservice),
Insecure: *connectinsecure,
TLSConfig: connecttlsconfig,
}
connector = &proxy.FixedConnector{
ComputeAddr: *connectaddr,
ServiceConfig: svcconfig,
}
default:
fmt.Fprintf(os.Stderr, "Unknown connector type %s\n", *connectortype)
}
var listentlsconfig *tls.Config
if !*listeninsecure {
var err error
listentlsconfig, err = loadTLSConfig(*listentlscert, *listentlskey, *listentlsca, "")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
glog.V(1).Info("Starting console server")
server := proxy.NewConsoleServer(*listenaddr, *listeninsecure, listentlsconfig, connector)
err := server.Serve()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
}
/*
* This file is part of the libvirt-console-proxy project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Copyright (C) 2016 Red Hat, Inc.
*
*/
package libvirtconsoleproxy
type ConsoleClient interface {
Proxy(*ServiceConfig) error
}
/*
* This file is part of the libvirt-console-proxy project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Copyright (C) 2016 Red Hat, Inc.
*
*/
package libvirtconsoleproxy
import (
"golang.org/x/net/websocket"
"net"
)
type ConnectorType string
const (
CONNECTOR_FIXED = "fixed"
)
type Connector interface {
Associate(tenant *websocket.Conn) (net.Conn, *ServiceConfig, error)
}
// FixedConnector allows tenants to connect to a single fixed
// compute node console server
type FixedConnector struct {
ComputeAddr string
ServiceConfig *ServiceConfig
}
func (c *FixedConnector) Associate(tenant *websocket.Conn) (net.Conn, *ServiceConfig, error) {
conn, err := net.Dial("tcp", c.ComputeAddr)
if err != nil {
return nil, nil, err
}
return conn, c.ServiceConfig, nil
}
/*
* This file is part of the libvirt-console-proxy project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Copyright (C) 2016 Red Hat, Inc.
*
*/
package libvirtconsoleproxy
import (
"crypto/tls"
"golang.org/x/net/websocket"
"net"
)
type ConsoleClientSerial struct {
Tenant net.Conn
Compute net.Conn
}
func NewConsoleClientSerial(tenant *websocket.Conn, compute net.Conn) *ConsoleClientSerial {
client := &ConsoleClientSerial{
Tenant: tenant,
Compute: compute,
}
tenant.PayloadType = websocket.BinaryFrame
return client
}
func (c *ConsoleClientSerial) proxyData(src net.Conn, dst net.Conn) error {
data := make([]byte, 64*1024)
pending := 0
for {
if pending == 0 {
var err error
pending, err = src.Read(data)
if err != nil {
return err
}
if pending == 0 {
return nil
}
}
done, err := dst.Write(data[0:pending])
if err != nil {
return err
}
data = data[done:]
pending -= done
}
}
func (c *ConsoleClientSerial) proxyToCompute() error {
err := c.proxyData(c.Tenant, c.Compute)
c.Compute.Close()
return err
}
func (c *ConsoleClientSerial) proxyToTenant() error {
err := c.proxyData(c.Compute, c.Tenant)
c.Tenant.Close()
return err
}
func (c *ConsoleClientSerial) Proxy(config *ServiceConfig) error {
if !config.Insecure {
conn := tls.Client(c.Compute, config.TLSConfig)
if err := conn.Handshake(); err != nil {
return err
}
c.Compute = conn
}
go c.proxyToTenant()
return c.proxyToCompute()
}
/*
* This file is part of the libvirt-console-proxy project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Copyright (C) 2016 Red Hat, Inc.
*
*/
package libvirtconsoleproxy
import (
"crypto/tls"
"fmt"
"github.com/golang/glog"
"golang.org/x/net/websocket"
"net/http"
"os"
)
type ConsoleServer struct {
Server *http.Server
WSServer *websocket.Server
Connector Connector
Mux *http.ServeMux
Insecure bool
TLSConfig *tls.Config
}
func NewConsoleServer(listenAddr string, insecure bool, tlsConfig *tls.Config, connector Connector) *ConsoleServer {
s := &ConsoleServer{
Mux: http.NewServeMux(),
Connector: connector,
Insecure: insecure,
TLSConfig: tlsConfig,
}
s.WSServer = &websocket.Server{
Handler: s.handleClient,
}
s.Server = &http.Server{
Addr: listenAddr,
TLSConfig: tlsConfig,
Handler: s.Mux,
}
s.Mux.Handle("/", s.WSServer)
s.Mux.Handle("/websockify", s.WSServer)
return s
}
func (s *ConsoleServer) Serve() error {
if s.Insecure {
err := s.Server.ListenAndServe()
if err != nil {
return err
}
} else {
err := s.Server.ListenAndServeTLS("", "")
if err != nil {
return err
}
}
return nil
}
func (s *ConsoleServer) handleClient(tenant *websocket.Conn) {
glog.V(1).Infof("New tenant connection")
compute, config, err := s.Connector.Associate(tenant)
if err != nil {
tenant.Close()
fmt.Fprintln(os.Stderr, err)
return
}
glog.V(1).Infof("Associated to compute service %s",
config.Type)
var client ConsoleClient
switch config.Type {
case SERVICE_VNC:
client = NewConsoleClientVNC(tenant, compute)
case SERVICE_SPICE:
client = NewConsoleClientSPICE(tenant, compute)
case SERVICE_SERIAL:
client = NewConsoleClientSerial(tenant, compute)
default:
fmt.Fprintln(os.Stderr, "Unexpected service type '%s'", config.Type)
}
err = client.Proxy(config)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
}
/*
* This file is part of the libvirt-console-proxy project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Copyright (C) 2016 Red Hat, Inc.
*
*/
package libvirtconsoleproxy
import (
"crypto/tls"
)
type ServiceType string
const (
SERVICE_VNC = ServiceType("vnc")
SERVICE_SPICE = ServiceType("spice")
SERVICE_SERIAL = ServiceType("serial")
)
type ServiceConfig struct {
Type ServiceType