mirror of https://github.com/moparisthebest/sslh
v1.6: 25APR2009
Added -V, version option. Install target directory configurable in Makefile Changed syslog prefix in auth.log to "sslh[%pid]" Man page new 'make install' and 'make install-debian' targets PID file now specified using -P command line option Actually fixed zombie generation (the v1.5 patch got lost, doh!)
This commit is contained in:
parent
b965d735b8
commit
0658982705
32
Makefile
32
Makefile
|
@ -1,23 +1,49 @@
|
||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
|
VERSION="v1.6i"
|
||||||
USELIBWRAP=1 # Use libwrap?
|
USELIBWRAP=1 # Use libwrap?
|
||||||
|
PREFIX=/usr/local
|
||||||
|
|
||||||
|
MAN=sslh.8.gz # man page name
|
||||||
|
|
||||||
# End of configuration -- the rest should take care of
|
# End of configuration -- the rest should take care of
|
||||||
# itself
|
# itself
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
|
CFLAGS=-Wall
|
||||||
|
|
||||||
#LIBS=-lnet
|
#LIBS=-lnet
|
||||||
LIBS=
|
LIBS=
|
||||||
|
|
||||||
ifneq ($(strip $(USELIBWRAP)),)
|
ifneq ($(strip $(USELIBWRAP)),)
|
||||||
LIBS:=$(LIBS) -lwrap
|
LIBS:=$(LIBS) -lwrap
|
||||||
CFLAGS=-DLIBWRAP
|
CFLAGS:=$(CFLAGS) -DLIBWRAP
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all:
|
all: sslh $(MAN)
|
||||||
$(CC) $(CFLAGS) -o sslh sslh.c $(LIBS)
|
|
||||||
|
sslh: sslh.c Makefile
|
||||||
|
$(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -o sslh sslh.c $(LIBS)
|
||||||
strip sslh
|
strip sslh
|
||||||
|
|
||||||
|
$(MAN): sslh.pod Makefile
|
||||||
|
pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
|
||||||
|
|
||||||
|
# generic install: install binary and man page
|
||||||
|
install: sslh $(MAN)
|
||||||
|
install -D sslh $(PREFIX)/sbin/sslh
|
||||||
|
install -D -m 0644 $(MAN) $(PREFIX)/share/man/man8/$(MAN)
|
||||||
|
|
||||||
|
# "extended" install for Debian: install startup script
|
||||||
|
install-debian: install sslh $(MAN)
|
||||||
|
sed -e "s+^PREFIX=+PREFIX=$(PREFIX)+" scripts/etc.init.d.sslh > /etc/init.d/sslh
|
||||||
|
chmod 755 /etc/init.d/sslh
|
||||||
|
cp scripts/etc.default.sslh /etc/default/sslh
|
||||||
|
update-rc.d sslh defaults
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f $(PREFIX)/sbin/sslh $(PREFIX)/share/man/man8/$(MAN) /etc/init.d/sslh /etc/default/sslh
|
||||||
|
update-rc.d sslh remove
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f sslh $(MAN)
|
||||||
|
|
44
README
44
README
|
@ -1,10 +1,20 @@
|
||||||
sslh -- A ssl/ssh multiplexer.
|
===== sslh -- A ssl/ssh multiplexer. =====
|
||||||
|
|
||||||
sslh lets one accept both HTTPS and SSH connections on the
|
sslh lets one accept both HTTPS and SSH connections on the
|
||||||
same port. It makes it possible to connect to an SSH server
|
same port. It makes it possible to connect to an SSH server
|
||||||
on port 443 (e.g. from inside a corporate firewall) while
|
on port 443 (e.g. from inside a corporate firewall) while
|
||||||
still serving HTTPS on that port.
|
still serving HTTPS on that port.
|
||||||
|
|
||||||
|
==== Compile and install ====
|
||||||
|
|
||||||
|
If you're lucky, the Makefile will work for you:
|
||||||
|
|
||||||
|
make install
|
||||||
|
|
||||||
|
(see below for configuration hints)
|
||||||
|
|
||||||
|
|
||||||
|
Otherwise:
|
||||||
|
|
||||||
Compilation instructions:
|
Compilation instructions:
|
||||||
|
|
||||||
|
@ -29,18 +39,29 @@ cp sslh /usr/local/sbin
|
||||||
cp scripts/etc.init.d.sslh /etc/init.d/sslh
|
cp scripts/etc.init.d.sslh /etc/init.d/sslh
|
||||||
cp scripts/etc.default.sslh /etc/default/sslh
|
cp scripts/etc.default.sslh /etc/default/sslh
|
||||||
|
|
||||||
|
and probably create links in /etc/rc<x>.d so that the server
|
||||||
|
start automatically at boot-up, e.g. under Debian:
|
||||||
|
update-rc.d sslh defaults
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==== Configuration ====
|
||||||
|
|
||||||
You can edit settings in /etc/default/sslh:
|
You can edit settings in /etc/default/sslh:
|
||||||
|
|
||||||
PIDFILE=/var/run/sslh.pid
|
|
||||||
LISTEN=ifname:443
|
LISTEN=ifname:443
|
||||||
SSH=localhost:22
|
SSH=localhost:22
|
||||||
SSL=localhost:443
|
SSL=localhost:443
|
||||||
|
|
||||||
A good scheme is to use the external name of the machine in
|
A good scheme is to use the external name of the machine in
|
||||||
$LISTEN, and bind httpd to localhost:443: that way, https
|
$LISTEN, and bind httpd to localhost:443 (instead of all
|
||||||
connections coming from inside your network don't need to go
|
binding to all interfaces): that way, https connections
|
||||||
through sslh, and sslh is only there as a frontal for
|
coming from inside your network don't need to go through
|
||||||
connections coming from the internet.
|
sslh, and sslh is only there as a frontal for connections
|
||||||
|
coming from the internet.
|
||||||
|
|
||||||
|
|
||||||
|
==== Libwrap support ====
|
||||||
|
|
||||||
Sslh can optionnaly perform libwrap checks for the sshd
|
Sslh can optionnaly perform libwrap checks for the sshd
|
||||||
service: because the connection to sshd will be coming
|
service: because the connection to sshd will be coming
|
||||||
|
@ -51,6 +72,17 @@ Comments? questions? sslh@rutschle.net
|
||||||
|
|
||||||
HISTORY
|
HISTORY
|
||||||
|
|
||||||
|
v1.6: 25APR2009
|
||||||
|
Added -V, version option.
|
||||||
|
Install target directory configurable in Makefile
|
||||||
|
Changed syslog prefix in auth.log to "sslh[%pid]"
|
||||||
|
Man page
|
||||||
|
new 'make install' and 'make install-debian' targets
|
||||||
|
PID file now specified using -P command line option
|
||||||
|
Actually fixed zombie generation (the v1.5 patch got
|
||||||
|
lost, doh!)
|
||||||
|
|
||||||
|
|
||||||
v1.5: 10DEC2008
|
v1.5: 10DEC2008
|
||||||
Fixed zombie generation.
|
Fixed zombie generation.
|
||||||
Added support scripts (), Makefile.
|
Added support scripts (), Makefile.
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
PIDFILE=/var/run/sslh.pid
|
|
||||||
LISTEN=ifname:443
|
LISTEN=ifname:443
|
||||||
SSH=localhost:22
|
SSH=localhost:22
|
||||||
SSL=localhost:443
|
SSL=localhost:443
|
||||||
|
|
|
@ -14,17 +14,18 @@ facility=user.info
|
||||||
# /etc/init.d/sslh: start and stop the sslh proxy daemon
|
# /etc/init.d/sslh: start and stop the sslh proxy daemon
|
||||||
|
|
||||||
# Defaults -- can be overridden in /etc/default/sslh
|
# Defaults -- can be overridden in /etc/default/sslh
|
||||||
PIDFILE=/var/run/sslh.pid
|
|
||||||
LISTEN=thelonious:443
|
LISTEN=thelonious:443
|
||||||
SSH=localhost:22
|
SSH=localhost:22
|
||||||
SSL=localhost:443
|
SSL=localhost:443
|
||||||
|
|
||||||
if test -f /etc/default/sslh; then
|
if test -f /etc/default/sslh; then
|
||||||
. /etc/default/sslh
|
. /etc/default/sslh
|
||||||
export PIDFILE=${PIDFILE}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DAEMON=/usr/local/sbin/sslh
|
# The prefix is normally filled by make install. If
|
||||||
|
# installing by hand, fill it in yourself!
|
||||||
|
PREFIX=
|
||||||
|
DAEMON=$PREFIX/sbin/sslh
|
||||||
|
|
||||||
start()
|
start()
|
||||||
{
|
{
|
||||||
|
|
68
sslh.c
68
sslh.c
|
@ -20,8 +20,7 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define VERSION "1.5"
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -36,6 +35,7 @@
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#ifdef LIBWRAP
|
#ifdef LIBWRAP
|
||||||
#include <tcpd.h>
|
#include <tcpd.h>
|
||||||
|
@ -50,15 +50,16 @@ if (res == -1) { \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define USAGE_STRING \
|
#define USAGE_STRING \
|
||||||
"sslh v" VERSION "\n" \
|
"sslh " VERSION "\n" \
|
||||||
"usage:\n" \
|
"usage:\n" \
|
||||||
"\texport PIDFILE=/var/run/sslhc.pid\n" \
|
|
||||||
"\tsslh [-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
|
"\tsslh [-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
|
||||||
"\t\t-s [sshhost:]port -l [sslhost:]port [-v]\n\n" \
|
"\t\t-s [sshhost:]port -l [sslhost:]port [-P pidfile] [-v] [-V]\n\n" \
|
||||||
"-v: verbose\n" \
|
"-v: verbose\n" \
|
||||||
|
"-V: version\n" \
|
||||||
"-p: address and port to listen on. default: 0.0.0.0:443\n" \
|
"-p: address and port to listen on. default: 0.0.0.0:443\n" \
|
||||||
"-s: SSH address: where to connect an SSH connection. default: localhost:22\n" \
|
"-s: SSH address: where to connect an SSH connection. default: localhost:22\n" \
|
||||||
"-l: SSL address: where to connect an SSL connection.\n" \
|
"-l: SSL address: where to connect an SSL connection.\n" \
|
||||||
|
"-P: PID file. Default: /var/run/sslh.pid\n" \
|
||||||
""
|
""
|
||||||
|
|
||||||
int verbose = 0; /* That's really quite global */
|
int verbose = 0; /* That's really quite global */
|
||||||
|
@ -188,7 +189,7 @@ void resolve_name(struct sockaddr *sock, char* fullname) {
|
||||||
|
|
||||||
res = getaddrinfo(host, serv, &hint, &addr);
|
res = getaddrinfo(host, serv, &hint, &addr);
|
||||||
if (res) {
|
if (res) {
|
||||||
fprintf(stderr, "%s\n", gai_strerror(res));
|
fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname);
|
||||||
if (res == EAI_SERVICE)
|
if (res == EAI_SERVICE)
|
||||||
fprintf(stderr, "(Check you have specified all ports)\n");
|
fprintf(stderr, "(Check you have specified all ports)\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -306,25 +307,31 @@ void start_shoveler(int in_socket)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SIGCHLD handling:
|
|
||||||
* we need to reap our children
|
|
||||||
*/
|
|
||||||
void child_handler(int signo)
|
|
||||||
{
|
|
||||||
signal(SIGCHLD, &child_handler);
|
|
||||||
wait(NULL);
|
|
||||||
}
|
|
||||||
void setup_signals(void)
|
void setup_signals(void)
|
||||||
{
|
{
|
||||||
void* res;
|
int res;
|
||||||
|
struct sigaction action;
|
||||||
|
|
||||||
res = signal(SIGCHLD, &child_handler);
|
/* Request no SIGCHLD is sent upon termination of
|
||||||
if (res == SIG_ERR) {
|
* the children */
|
||||||
perror("signal");
|
memset(&action, 0, sizeof(action));
|
||||||
exit(1);
|
action.sa_handler = NULL;
|
||||||
}
|
action.sa_flags = SA_NOCLDWAIT;
|
||||||
|
res = sigaction(SIGCHLD, &action, NULL);
|
||||||
|
CHECK_RES_DIE(res, "sigaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Open syslog connection with appropriate banner;
|
||||||
|
* banner is made up of basename(bin_name)+"[pid]" */
|
||||||
|
void setup_syslog(char* bin_name) {
|
||||||
|
char *name1, *name2;
|
||||||
|
|
||||||
|
name1 = strdup(bin_name);
|
||||||
|
asprintf(&name2, "%s[%d]", basename(name1), getpid());
|
||||||
|
openlog(name2, LOG_CONS, LOG_AUTH);
|
||||||
|
free(name1);
|
||||||
|
/* Don't free name2, as openlog(3) uses it (at least in glibc) */
|
||||||
|
}
|
||||||
|
|
||||||
/* We don't want to run as root -- drop priviledges if required */
|
/* We don't want to run as root -- drop priviledges if required */
|
||||||
void drop_privileges(char* user_name)
|
void drop_privileges(char* user_name)
|
||||||
|
@ -345,14 +352,10 @@ void drop_privileges(char* user_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Writes my PID if $PIDFILE is defined */
|
/* Writes my PID if $PIDFILE is defined */
|
||||||
void write_pid_file(void)
|
void write_pid_file(char* pidfile)
|
||||||
{
|
{
|
||||||
char *pidfile = getenv("PIDFILE");
|
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
|
||||||
if (!pidfile)
|
|
||||||
return;
|
|
||||||
|
|
||||||
f = fopen(pidfile, "w");
|
f = fopen(pidfile, "w");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
perror(pidfile);
|
perror(pidfile);
|
||||||
|
@ -391,12 +394,13 @@ int main(int argc, char *argv[])
|
||||||
char listen_str[] = "0.0.0.0:443";
|
char listen_str[] = "0.0.0.0:443";
|
||||||
char ssl_str[] = "localhost:442";
|
char ssl_str[] = "localhost:442";
|
||||||
char ssh_str[] = "localhost:22";
|
char ssh_str[] = "localhost:22";
|
||||||
|
char *pid_file = "/var/run/sslh.pid";
|
||||||
|
|
||||||
resolve_name(&addr_listen, listen_str);
|
resolve_name(&addr_listen, listen_str);
|
||||||
resolve_name(&addr_ssl, ssl_str);
|
resolve_name(&addr_ssl, ssl_str);
|
||||||
resolve_name(&addr_ssh, ssh_str);
|
resolve_name(&addr_ssh, ssh_str);
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "t:l:s:p:vu:")) != EOF) {
|
while ((c = getopt(argc, argv, "t:l:s:p:P:vVu:")) != EOF) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
|
@ -419,10 +423,18 @@ int main(int argc, char *argv[])
|
||||||
verbose += 1;
|
verbose += 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
printf("sslh %s\n", VERSION);
|
||||||
|
exit(0);
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
user_name = optarg;
|
user_name = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
pid_file = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, USAGE_STRING);
|
fprintf(stderr, USAGE_STRING);
|
||||||
exit(2);
|
exit(2);
|
||||||
|
@ -438,7 +450,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
if (fork() > 0) exit(0); /* Detach */
|
if (fork() > 0) exit(0); /* Detach */
|
||||||
|
|
||||||
write_pid_file();
|
write_pid_file(pid_file);
|
||||||
|
|
||||||
drop_privileges(user_name);
|
drop_privileges(user_name);
|
||||||
|
|
||||||
|
@ -447,7 +459,7 @@ int main(int argc, char *argv[])
|
||||||
CHECK_RES_DIE(res, "setsid: already process leader");
|
CHECK_RES_DIE(res, "setsid: already process leader");
|
||||||
|
|
||||||
/* Open syslog connection */
|
/* Open syslog connection */
|
||||||
openlog(argv[0], LOG_CONS, LOG_AUTH);
|
setup_syslog(argv[0]);
|
||||||
|
|
||||||
/* Main server loop: accept connections, find what they are, fork shovelers */
|
/* Main server loop: accept connections, find what they are, fork shovelers */
|
||||||
while (1)
|
while (1)
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
# I'm just not gonna write troff :-)
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
sslh - ssl/ssh multiplexer
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
sslh [ B<-t> I<num> ] [B<-p> I<listening address>] [B<-l> I<target address for SSL>] [B<-s> I<target address for SSH>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-V]
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
B<sslh> lets one accept both HTTPS and SSH connections on
|
||||||
|
the same port. It makes it possible to connect to an SSH
|
||||||
|
server on port 443 (e.g. from inside a corporate firewall,
|
||||||
|
which almost never block port 443) while still serving HTTPS
|
||||||
|
on that port.
|
||||||
|
|
||||||
|
The idea is to have B<sslh> listen to the external 443 port,
|
||||||
|
accept the incoming connections, work out what type of
|
||||||
|
connection it is, and then fordward to the appropriate
|
||||||
|
server.
|
||||||
|
|
||||||
|
=head2 Protocol detection
|
||||||
|
|
||||||
|
The protocol detection is made based on a small difference
|
||||||
|
between SSL and SSH: an SSL client connecting to a server
|
||||||
|
speaks first, whereas an SSH client expects the SSH server
|
||||||
|
to speak first (announcing itself with a banner). B<sslh>
|
||||||
|
waits for some time for the incoming connection to send data.
|
||||||
|
If it does before the timeout occurs, it is supposed to be
|
||||||
|
an SSL connection. Otherwise, it is supposed to be an SSH
|
||||||
|
connection.
|
||||||
|
|
||||||
|
=head2 Libwrap support
|
||||||
|
|
||||||
|
One drawback of B<sslh> is that the B<ssh> and B<httpd>
|
||||||
|
servers do not see the original IP address of the client
|
||||||
|
anymore, as the connection is forwarded through B<sslh>.
|
||||||
|
B<sslh> provides enough logging to circumvent that problem.
|
||||||
|
However it is common to limit access to B<ssh> using
|
||||||
|
B<libwrap> or B<tcpd>. For this reason, B<sslh> can be
|
||||||
|
compiled to check SSH accesses against SSH access lists as
|
||||||
|
defined in F</etc/hosts.allow> and F</etc/hosts.deny>.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item B<-t> I<num>
|
||||||
|
|
||||||
|
Timeout before a connection is considered to be SSH. Default
|
||||||
|
is 2s.
|
||||||
|
|
||||||
|
=item B<-p> I<listening address>
|
||||||
|
|
||||||
|
Interface and port on which to listen, e.g. I<foobar:443>,
|
||||||
|
where I<foobar> is the name of an interface (typically the
|
||||||
|
IP address on which the Internet connection ends up).
|
||||||
|
|
||||||
|
Defaults to I<0.0.0.0:443> (listen to port 443 on all
|
||||||
|
available interfaces).
|
||||||
|
|
||||||
|
=item B<-l> I<target address for SSL>
|
||||||
|
|
||||||
|
Interface and port on which to forward SSL connection,
|
||||||
|
typically I<localhost:443>.
|
||||||
|
|
||||||
|
Defaults to I<localhost:442> (this assumes you would
|
||||||
|
configure your B<httpd> process to listen to port 443).
|
||||||
|
|
||||||
|
Note that you can set B<sslh> to listen on I<ext_ip:443> and
|
||||||
|
B<httpd> to listen on I<localhost:443>: this allows clients
|
||||||
|
inside your network to just connect directly to B<httpd>.
|
||||||
|
|
||||||
|
=item B<-s> I<target address for SSH>
|
||||||
|
|
||||||
|
Interface and port on which to forward SSH connection,
|
||||||
|
defaults to I<localhost:22>.
|
||||||
|
|
||||||
|
=item B<-v>
|
||||||
|
|
||||||
|
Increase verboseness.
|
||||||
|
|
||||||
|
=item B<-V>
|
||||||
|
|
||||||
|
Prints B<sslh> version.
|
||||||
|
|
||||||
|
=item B<-u> I<username>
|
||||||
|
|
||||||
|
Requires to run under the specified username. Defaults to
|
||||||
|
I<nobody> (which is not perfect -- ideally B<sslh> should
|
||||||
|
run under its own UID).
|
||||||
|
|
||||||
|
=item B<-P> I<pidfile>
|
||||||
|
|
||||||
|
Specifies the file in which to write the PID of the main
|
||||||
|
server. Defaults to I</var/run/sslh.pid>.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 FILES
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item F</etc/init.d/sslh>
|
||||||
|
|
||||||
|
Start-up script. The standard actions B<start>, B<stop> and
|
||||||
|
B<restart> are supported.
|
||||||
|
|
||||||
|
=item F</etc/default/sslh>
|
||||||
|
|
||||||
|
Server configuration. These are environement variables
|
||||||
|
loaded by the start-up script and passed to B<sslh> as
|
||||||
|
command-line arguments. Refer to the OPTIONS section for a
|
||||||
|
detailed explanation of the variables used by B<sslh>.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
Last version available from
|
||||||
|
L<http://www.rutschle.net/tech/sslh>, and can be tracked
|
||||||
|
from L<http://freshmeat.net/projects/sslh/>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Written by Yves Rutschle
|
Loading…
Reference in New Issue