Make internal SSH server host key path configurable (#14918)
* Make SSH server host key path configurable * make it possible to have multiple keys * Make gitea.rsa the default key * Add some more logging Signed-off-by: Andrew Thornton <art27@cantab.net>release/v1.15
parent
78b7529cd4
commit
c03f530212
12
cmd/web.go
12
cmd/web.go
|
@ -64,7 +64,7 @@ func runHTTPRedirector() {
|
||||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
|
|
||||||
var err = runHTTP("tcp", source, context2.ClearHandler(handler))
|
var err = runHTTP("tcp", source, "HTTP Redirector", context2.ClearHandler(handler))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to start port redirection: %v", err)
|
log.Fatal("Failed to start port redirection: %v", err)
|
||||||
|
@ -198,7 +198,7 @@ func listen(m http.Handler, handleRedirector bool) error {
|
||||||
if handleRedirector {
|
if handleRedirector {
|
||||||
NoHTTPRedirector()
|
NoHTTPRedirector()
|
||||||
}
|
}
|
||||||
err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
|
err = runHTTP("tcp", listenAddr, "Web", context2.ClearHandler(m))
|
||||||
case setting.HTTPS:
|
case setting.HTTPS:
|
||||||
if setting.EnableLetsEncrypt {
|
if setting.EnableLetsEncrypt {
|
||||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
||||||
|
@ -211,22 +211,22 @@ func listen(m http.Handler, handleRedirector bool) error {
|
||||||
NoHTTPRedirector()
|
NoHTTPRedirector()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||||
case setting.FCGI:
|
case setting.FCGI:
|
||||||
if handleRedirector {
|
if handleRedirector {
|
||||||
NoHTTPRedirector()
|
NoHTTPRedirector()
|
||||||
}
|
}
|
||||||
err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
|
err = runFCGI("tcp", listenAddr, "FCGI Web", context2.ClearHandler(m))
|
||||||
case setting.UnixSocket:
|
case setting.UnixSocket:
|
||||||
if handleRedirector {
|
if handleRedirector {
|
||||||
NoHTTPRedirector()
|
NoHTTPRedirector()
|
||||||
}
|
}
|
||||||
err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
|
err = runHTTP("unix", listenAddr, "Web", context2.ClearHandler(m))
|
||||||
case setting.FCGIUnix:
|
case setting.FCGIUnix:
|
||||||
if handleRedirector {
|
if handleRedirector {
|
||||||
NoHTTPRedirector()
|
NoHTTPRedirector()
|
||||||
}
|
}
|
||||||
err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
|
err = runFCGI("unix", listenAddr, "Web", context2.ClearHandler(m))
|
||||||
default:
|
default:
|
||||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,16 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runHTTP(network, listenAddr string, m http.Handler) error {
|
func runHTTP(network, listenAddr, name string, m http.Handler) error {
|
||||||
return graceful.HTTPListenAndServe(network, listenAddr, m)
|
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHTTPS(network, listenAddr, certFile, keyFile string, m http.Handler) error {
|
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
|
||||||
return graceful.HTTPListenAndServeTLS(network, listenAddr, certFile, keyFile, m)
|
return graceful.HTTPListenAndServeTLS(network, listenAddr, name, certFile, keyFile, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHTTPSWithTLSConfig(network, listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
|
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
||||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, tlsConfig, m)
|
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||||
|
@ -43,9 +43,9 @@ func NoInstallListener() {
|
||||||
graceful.GetManager().InformCleanup()
|
graceful.GetManager().InformCleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFCGI(network, listenAddr string, m http.Handler) error {
|
func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
||||||
// This needs to handle stdin as fcgi point
|
// This needs to handle stdin as fcgi point
|
||||||
fcgiServer := graceful.NewServer(network, listenAddr)
|
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
||||||
|
|
||||||
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
||||||
return fcgi.Serve(listener, m)
|
return fcgi.Serve(listener, m)
|
||||||
|
|
|
@ -46,14 +46,14 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
|
||||||
go func() {
|
go func() {
|
||||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||||
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
return runHTTPSWithTLSConfig("tcp", listenAddr, tlsConfig, context2.ClearHandler(m))
|
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, context2.ClearHandler(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -319,6 +319,10 @@ SSH_SERVER_KEY_EXCHANGES = diffie-hellman-group1-sha1, diffie-hellman-group14-sh
|
||||||
; For the built-in SSH server, choose the MACs to support for SSH connections,
|
; For the built-in SSH server, choose the MACs to support for SSH connections,
|
||||||
; for system SSH this setting has no effect
|
; for system SSH this setting has no effect
|
||||||
SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96
|
SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96
|
||||||
|
; For the built-in SSH server, choose the keypair to offer as the host key
|
||||||
|
; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
|
||||||
|
; relative paths are made absolute relative to the APP_DATA_PATH
|
||||||
|
SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
|
||||||
; Directory to create temporary files in when testing public keys using ssh-keygen,
|
; Directory to create temporary files in when testing public keys using ssh-keygen,
|
||||||
; default is the system temporary directory.
|
; default is the system temporary directory.
|
||||||
SSH_KEY_TEST_PATH =
|
SSH_KEY_TEST_PATH =
|
||||||
|
|
|
@ -256,6 +256,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
||||||
- `SSH_SERVER_CIPHERS`: **aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect.
|
- `SSH_SERVER_CIPHERS`: **aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect.
|
||||||
- `SSH_SERVER_KEY_EXCHANGES`: **diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, curve25519-sha256@libssh.org**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect.
|
- `SSH_SERVER_KEY_EXCHANGES`: **diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, curve25519-sha256@libssh.org**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect.
|
||||||
- `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect
|
- `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect
|
||||||
|
- `SSH_SERVER_HOST_KEYS`: **ssh/gitea.rsa, ssh/gogs.rsa**: For the built-in SSH server, choose the keypairs to offer as the host key. The private key should be at `SSH_SERVER_HOST_KEY` and the public `SSH_SERVER_HOST_KEY.pub`. Relative paths are made absolute relative to the `APP_DATA_PATH`. If no key exists a 4096 bit RSA key will be created for you.
|
||||||
- `SSH_KEY_TEST_PATH`: **/tmp**: Directory to create temporary files in when testing public keys using ssh-keygen, default is the system temporary directory.
|
- `SSH_KEY_TEST_PATH`: **/tmp**: Directory to create temporary files in when testing public keys using ssh-keygen, default is the system temporary directory.
|
||||||
- `SSH_KEYGEN_PATH`: **ssh-keygen**: Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call.
|
- `SSH_KEYGEN_PATH`: **ssh-keygen**: Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call.
|
||||||
- `SSH_EXPOSE_ANONYMOUS`: **false**: Enable exposure of SSH clone URL to anonymous visitors, default is false.
|
- `SSH_EXPOSE_ANONYMOUS`: **false**: Enable exposure of SSH clone URL to anonymous visitors, default is false.
|
||||||
|
|
|
@ -48,11 +48,11 @@ type Server struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a server on network at provided address
|
// NewServer creates a server on network at provided address
|
||||||
func NewServer(network, address string) *Server {
|
func NewServer(network, address, name string) *Server {
|
||||||
if GetManager().IsChild() {
|
if GetManager().IsChild() {
|
||||||
log.Info("Restarting new server: %s:%s on PID: %d", network, address, os.Getpid())
|
log.Info("Restarting new %s server: %s:%s on PID: %d", name, network, address, os.Getpid())
|
||||||
} else {
|
} else {
|
||||||
log.Info("Starting new server: %s:%s on PID: %d", network, address, os.Getpid())
|
log.Info("Starting new %s server: %s:%s on PID: %d", name, network, address, os.Getpid())
|
||||||
}
|
}
|
||||||
srv := &Server{
|
srv := &Server{
|
||||||
wg: sync.WaitGroup{},
|
wg: sync.WaitGroup{},
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newHTTPServer(network, address string, handler http.Handler) (*Server, ServeFunction) {
|
func newHTTPServer(network, address, name string, handler http.Handler) (*Server, ServeFunction) {
|
||||||
server := NewServer(network, address)
|
server := NewServer(network, address, name)
|
||||||
httpServer := http.Server{
|
httpServer := http.Server{
|
||||||
ReadTimeout: DefaultReadTimeOut,
|
ReadTimeout: DefaultReadTimeOut,
|
||||||
WriteTimeout: DefaultWriteTimeOut,
|
WriteTimeout: DefaultWriteTimeOut,
|
||||||
|
@ -25,21 +25,21 @@ func newHTTPServer(network, address string, handler http.Handler) (*Server, Serv
|
||||||
|
|
||||||
// HTTPListenAndServe listens on the provided network address and then calls Serve
|
// HTTPListenAndServe listens on the provided network address and then calls Serve
|
||||||
// to handle requests on incoming connections.
|
// to handle requests on incoming connections.
|
||||||
func HTTPListenAndServe(network, address string, handler http.Handler) error {
|
func HTTPListenAndServe(network, address, name string, handler http.Handler) error {
|
||||||
server, lHandler := newHTTPServer(network, address, handler)
|
server, lHandler := newHTTPServer(network, address, name, handler)
|
||||||
return server.ListenAndServe(lHandler)
|
return server.ListenAndServe(lHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPListenAndServeTLS listens on the provided network address and then calls Serve
|
// HTTPListenAndServeTLS listens on the provided network address and then calls Serve
|
||||||
// to handle requests on incoming connections.
|
// to handle requests on incoming connections.
|
||||||
func HTTPListenAndServeTLS(network, address, certFile, keyFile string, handler http.Handler) error {
|
func HTTPListenAndServeTLS(network, address, name, certFile, keyFile string, handler http.Handler) error {
|
||||||
server, lHandler := newHTTPServer(network, address, handler)
|
server, lHandler := newHTTPServer(network, address, name, handler)
|
||||||
return server.ListenAndServeTLS(certFile, keyFile, lHandler)
|
return server.ListenAndServeTLS(certFile, keyFile, lHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPListenAndServeTLSConfig listens on the provided network address and then calls Serve
|
// HTTPListenAndServeTLSConfig listens on the provided network address and then calls Serve
|
||||||
// to handle requests on incoming connections.
|
// to handle requests on incoming connections.
|
||||||
func HTTPListenAndServeTLSConfig(network, address string, tlsConfig *tls.Config, handler http.Handler) error {
|
func HTTPListenAndServeTLSConfig(network, address, name string, tlsConfig *tls.Config, handler http.Handler) error {
|
||||||
server, lHandler := newHTTPServer(network, address, handler)
|
server, lHandler := newHTTPServer(network, address, name, handler)
|
||||||
return server.ListenAndServeTLSConfig(tlsConfig, lHandler)
|
return server.ListenAndServeTLSConfig(tlsConfig, lHandler)
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,7 @@ var (
|
||||||
ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
|
ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
|
||||||
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
|
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
|
||||||
ServerMACs []string `ini:"SSH_SERVER_MACS"`
|
ServerMACs []string `ini:"SSH_SERVER_MACS"`
|
||||||
|
ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"`
|
||||||
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
|
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
|
||||||
KeygenPath string `ini:"SSH_KEYGEN_PATH"`
|
KeygenPath string `ini:"SSH_KEYGEN_PATH"`
|
||||||
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
|
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
|
||||||
|
@ -157,6 +158,7 @@ var (
|
||||||
KeygenPath: "ssh-keygen",
|
KeygenPath: "ssh-keygen",
|
||||||
MinimumKeySizeCheck: true,
|
MinimumKeySizeCheck: true,
|
||||||
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048},
|
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048},
|
||||||
|
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Security settings
|
// Security settings
|
||||||
|
@ -698,6 +700,11 @@ func NewContext() {
|
||||||
if err = Cfg.Section("server").MapTo(&SSH); err != nil {
|
if err = Cfg.Section("server").MapTo(&SSH); err != nil {
|
||||||
log.Fatal("Failed to map SSH settings: %v", err)
|
log.Fatal("Failed to map SSH settings: %v", err)
|
||||||
}
|
}
|
||||||
|
for i, key := range SSH.ServerHostKeys {
|
||||||
|
if !filepath.IsAbs(key) {
|
||||||
|
SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
|
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
|
||||||
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
|
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
|
||||||
|
|
|
@ -259,28 +259,38 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
keyPath := filepath.Join(setting.AppDataPath, "ssh/gogs.rsa")
|
keys := make([]string, 0, len(setting.SSH.ServerHostKeys))
|
||||||
isExist, err := util.IsExist(keyPath)
|
for _, key := range setting.SSH.ServerHostKeys {
|
||||||
if err != nil {
|
isExist, err := util.IsExist(key)
|
||||||
log.Fatal("Unable to check if %s exists. Error: %v", keyPath, err)
|
if err != nil {
|
||||||
|
log.Fatal("Unable to check if %s exists. Error: %v", setting.SSH.ServerHostKeys, err)
|
||||||
|
}
|
||||||
|
if isExist {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !isExist {
|
|
||||||
filePath := filepath.Dir(keyPath)
|
if len(keys) == 0 {
|
||||||
|
filePath := filepath.Dir(setting.SSH.ServerHostKeys[0])
|
||||||
|
|
||||||
if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
|
||||||
log.Error("Failed to create dir %s: %v", filePath, err)
|
log.Error("Failed to create dir %s: %v", filePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := GenKeyPair(keyPath)
|
err := GenKeyPair(setting.SSH.ServerHostKeys[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to generate private key: %v", err)
|
log.Fatal("Failed to generate private key: %v", err)
|
||||||
}
|
}
|
||||||
log.Trace("New private key is generated: %s", keyPath)
|
log.Trace("New private key is generated: %s", setting.SSH.ServerHostKeys[0])
|
||||||
|
keys = append(keys, setting.SSH.ServerHostKeys[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
err = srv.SetOption(ssh.HostKeyFile(keyPath))
|
for _, key := range keys {
|
||||||
if err != nil {
|
log.Info("Adding SSH host key: %s", key)
|
||||||
log.Error("Failed to set Host Key. %s", err)
|
err := srv.SetOption(ssh.HostKeyFile(key))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to set Host Key. %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go listen(&srv)
|
go listen(&srv)
|
||||||
|
@ -291,7 +301,7 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs
|
||||||
// Public key is encoded in the format for inclusion in an OpenSSH authorized_keys file.
|
// Public key is encoded in the format for inclusion in an OpenSSH authorized_keys file.
|
||||||
// Private Key generated is PEM encoded
|
// Private Key generated is PEM encoded
|
||||||
func GenKeyPair(keyPath string) error {
|
func GenKeyPair(keyPath string) error {
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func listen(server *ssh.Server) {
|
func listen(server *ssh.Server) {
|
||||||
gracefulServer := graceful.NewServer("tcp", server.Addr)
|
gracefulServer := graceful.NewServer("tcp", server.Addr, "SSH")
|
||||||
|
|
||||||
err := gracefulServer.ListenAndServe(server.Serve)
|
err := gracefulServer.ListenAndServe(server.Serve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue