From 79e06aeb557e0af7564d64987600ca2138ce04c3 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 30 Jun 2016 19:22:35 +0200 Subject: Add link to Boulder issue #359 (Implement Certificate Refresh). --- lacme | 1 + 1 file changed, 1 insertion(+) (limited to 'lacme') diff --git a/lacme b/lacme index f6d2b7a..3d98760 100755 --- a/lacme +++ b/lacme @@ -516,6 +516,7 @@ if ($COMMAND eq 'new-reg' or $COMMAND =~ /^reg=/) { # TODO: renewal without the account key, see # https://github.com/letsencrypt/acme-spec/pull/168 # https://github.com/letsencrypt/acme-spec/issues/191 +# https://github.com/letsencrypt/boulder/issues/359 # elsif ($COMMAND eq 'new-cert') { my $conf; -- cgit v1.2.3 From ec03eae5c2a39f4a878ad910eda3c414c6d2d51f Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 30 Nov 2016 21:55:19 +0100 Subject: Stop mentioning GET-based renewal, as it was removed from the ACME IETF draft. https://github.com/ietf-wg-acme/acme/issues/62 https://github.com/ietf-wg-acme/acme/pull/67 :-( --- lacme | 4 ---- 1 file changed, 4 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 3d98760..13bff78 100755 --- a/lacme +++ b/lacme @@ -513,10 +513,6 @@ if ($COMMAND eq 'new-reg' or $COMMAND =~ /^reg=/) { ############################################################################# # new-cert [SECTION ..] -# TODO: renewal without the account key, see -# https://github.com/letsencrypt/acme-spec/pull/168 -# https://github.com/letsencrypt/acme-spec/issues/191 -# https://github.com/letsencrypt/boulder/issues/359 # elsif ($COMMAND eq 'new-cert') { my $conf; -- cgit v1.2.3 From 27788fd4a399642eddbdb1934ccaa13f7fd00124 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 1 Dec 2016 00:16:18 +0100 Subject: Make lacme able to spawn lacme-accountd. --- lacme | 77 +++++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 20 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 13bff78..05b4b18 100755 --- a/lacme +++ b/lacme @@ -30,7 +30,7 @@ use File::Temp (); use Getopt::Long qw/:config posix_default no_ignore_case gnu_getopt auto_version/; use List::Util 'first'; use POSIX (); -use Socket qw/PF_INET PF_INET6 PF_UNIX INADDR_ANY IN6ADDR_ANY +use Socket qw/AF_UNIX PF_INET PF_INET6 PF_UNIX PF_UNSPEC INADDR_ANY IN6ADDR_ANY SOCK_STREAM SOL_SOCKET SO_REUSEADDR SHUT_RDWR/; use Config::Tiny (); @@ -82,6 +82,7 @@ do { my $h = Config::Tiny::->read_string($conf) or die Config::Tiny::->errstr()."\n"; my $defaults = delete $h->{_} // {}; + my $accountd = exists $h->{accountd} ? 1 : 0; my %valid = ( client => { socket => (defined $ENV{XDG_RUNTIME_DIR} ? "$ENV{XDG_RUNTIME_DIR}/S.lacme" : undef), @@ -99,6 +100,14 @@ do { command => '/usr/lib/lacme/webserver', iptables => 'Yes' + }, + accountd => { + user => '', + group => '', + command => '/usr/bin/lacme-accountd', + config => '/etc/lacme/lacme-accountd.conf', + privkey => undef, + quiet => 'Yes', } ); foreach my $s (keys %valid) { @@ -110,6 +119,7 @@ do { } die "Invalid section(s): ".join(', ', keys %$h)."\n" if %$h; $CONFIG->{_} = $defaults; + delete $CONFIG->{accountd} unless $accountd; }; # Regular expressions for domain validation @@ -388,31 +398,58 @@ sub acme_client($@) { my $args = shift; my @args = @_; - my @stat; + my $client; my $conf = $CONFIG->{client}; - my $sockname = $OPTS{socket} // $conf->{socket} // die "Missing socket option\n"; - $sockname = $sockname =~ /\A(\p{Print}+)\z/ ? $1 : die "Invalid socket name\n"; # untaint $sockname - - # ensure we're the only user with write access to the parent dir - my $dirname = $sockname =~ s/[^\/]+$//r; - @stat = stat($dirname) or die "Can't stat $dirname: $!"; - die "Error: insecure permissions on $dirname\n" if ($stat[2] & 0022) != 0; - - # ensure we're the only user with read/write access to the socket - @stat = stat($sockname) or die "Can't stat $sockname: $! (Is lacme-accountd running?)\n"; - die "Error: insecure permissions on $sockname\n" if ($stat[2] & 0066) != 0; - - # connect(2) to the socket - socket(my $client, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; - my $sockaddr = Socket::sockaddr_un($sockname) // die "Invalid address $sockname\n"; - until (connect($client, $sockaddr)) { - next if $! == EINTR; # try again if connect(2) was interrupted by a signal - die "connect: $!"; + if (defined (my $accountd = $CONFIG->{accountd})) { + socketpair($client, my $s, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; + my $pid = fork() // "fork: $!"; + unless ($pid) { + drop_privileges($accountd->{user}, $accountd->{group}, '/'); + set_FD_CLOEXEC($s, 0); + $client->close() or die "Can't close: $!"; + my @cmd = ($accountd->{command}, '--fdopen='.fileno($s)); + push @cmd, '--config='.$accountd->{config} if defined $accountd->{config}; + push @cmd, '--privkey='.$accountd->{privkey} if defined $accountd->{privkey}; + push @cmd, '--quiet' unless lc $accountd->{quiet} eq 'no'; + push @cmd, '--debug' if $OPTS{debug}; + exec { $cmd[0] } @cmd or die; + } + print STDERR "[$$] Forking lacme-accountd, child PID $pid\n" if $OPTS{debug}; + $s->close() or die "Can't close: $!"; + push @CLEANUP, sub() { + print STDERR "[$$] Shutting down lacme-accountd\n" if $OPTS{debug}; + shutdown($client, SHUT_RDWR) or warn "shutdown: $!"; + kill 15 => $pid; + waitpid $pid => 0; + }; + } + else { + my @stat; + my $sockname = $OPTS{socket} // $conf->{socket} // die "Missing socket option\n"; + $sockname = $sockname =~ /\A(\p{Print}+)\z/ ? $1 : die "Invalid socket name\n"; # untaint $sockname + + # ensure we're the only user with write access to the parent dir + my $dirname = $sockname =~ s/[^\/]+$//r; + @stat = stat($dirname) or die "Can't stat $dirname: $!"; + die "Error: insecure permissions on $dirname\n" if ($stat[2] & 0022) != 0; + + # ensure we're the only user with read/write access to the socket + @stat = stat($sockname) or die "Can't stat $sockname: $! (Is lacme-accountd running?)\n"; + die "Error: insecure permissions on $sockname\n" if ($stat[2] & 0066) != 0; + + # connect(2) to the socket + socket($client, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + my $sockaddr = Socket::sockaddr_un($sockname) // die "Invalid address $sockname\n"; + until (connect($client, $sockaddr)) { + next if $! == EINTR; # try again if connect(2) was interrupted by a signal + die "connect: $!"; + } } # use execve(2) rather than a Perl pseudo-process to ensure that the # child doesn't have access to the parent's memory my @fileno = map { fileno($_) =~ /^(\d+)$/ ? $1 : die } ($CONFFILE, $client); # untaint fileno + set_FD_CLOEXEC($client, 1); spawn({%$args{qw/in out/}, child => sub() { drop_privileges($conf->{user}, $conf->{group}, $args->{chdir} // '/'); set_FD_CLOEXEC($_, 0) foreach ($CONFFILE, $client); -- cgit v1.2.3 From 844edd3dd60590bafcaa863eedb6cda94a0e07a3 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 1 Dec 2016 00:37:52 +0100 Subject: lacme: add an option --quiet to avoid mentioning valid certs. --- lacme | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 05b4b18..839d53d 100755 --- a/lacme +++ b/lacme @@ -60,7 +60,7 @@ sub usage(;$$) { } exit $rv; } -usage(1) unless GetOptions(\%OPTS, qw/config=s config-certs=s socket=s agreement-uri=s debug help|h/); +usage(1) unless GetOptions(\%OPTS, qw/config=s config-certs=s socket=s agreement-uri=s quiet|q debug help|h/); usage(0) if $OPTS{help}; $COMMAND = shift(@ARGV) // usage(1, "Missing command"); @@ -120,6 +120,7 @@ do { die "Invalid section(s): ".join(', ', keys %$h)."\n" if %$h; $CONFIG->{_} = $defaults; delete $CONFIG->{accountd} unless $accountd; + $OPTS{quiet} = 0 if $OPTS{debug}; }; # Regular expressions for domain validation @@ -592,7 +593,7 @@ elsif ($COMMAND eq 'new-cert') { my $d = $conf->{'min-days'} // 10; if ($d > 0 and $t - time > $d*86400) { my $d = POSIX::strftime('%Y-%m-%d %H:%M:%S UTC', gmtime($t)); - print STDERR "[$s] Valid until $d, skipping\n"; + print STDERR "[$s] Valid until $d, skipping\n" unless $OPTS{quiet}; next; } } -- cgit v1.2.3 From 8faab5db6571972156f45b5838b23dbb0fadd5c4 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 1 Dec 2016 10:41:33 +0100 Subject: lacme: avoid spawning multiple accountd processes. --- lacme | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) (limited to 'lacme') diff --git a/lacme b/lacme index 839d53d..cf2f9eb 100755 --- a/lacme +++ b/lacme @@ -395,6 +395,7 @@ sub spawn_webserver() { # If $args->{in} is defined, the data is written to the client's STDIN. # If $args->{out} is defined, its value is set to client's STDOUT data. # +my $ACCOUNTD = 0; sub acme_client($@) { my $args = shift; my @args = @_; @@ -402,27 +403,30 @@ sub acme_client($@) { my $client; my $conf = $CONFIG->{client}; if (defined (my $accountd = $CONFIG->{accountd})) { - socketpair($client, my $s, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; - my $pid = fork() // "fork: $!"; - unless ($pid) { - drop_privileges($accountd->{user}, $accountd->{group}, '/'); - set_FD_CLOEXEC($s, 0); - $client->close() or die "Can't close: $!"; - my @cmd = ($accountd->{command}, '--fdopen='.fileno($s)); - push @cmd, '--config='.$accountd->{config} if defined $accountd->{config}; - push @cmd, '--privkey='.$accountd->{privkey} if defined $accountd->{privkey}; - push @cmd, '--quiet' unless lc $accountd->{quiet} eq 'no'; - push @cmd, '--debug' if $OPTS{debug}; - exec { $cmd[0] } @cmd or die; + unless ($ACCOUNTD) { + socketpair($client, my $s, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; + my $pid = fork() // "fork: $!"; + unless ($pid) { + drop_privileges($accountd->{user}, $accountd->{group}, '/'); + set_FD_CLOEXEC($s, 0); + $client->close() or die "Can't close: $!"; + my @cmd = ($accountd->{command}, '--fdopen='.fileno($s)); + push @cmd, '--config='.$accountd->{config} if defined $accountd->{config}; + push @cmd, '--privkey='.$accountd->{privkey} if defined $accountd->{privkey}; + push @cmd, '--quiet' unless lc $accountd->{quiet} eq 'no'; + push @cmd, '--debug' if $OPTS{debug}; + exec { $cmd[0] } @cmd or die; + } + print STDERR "[$$] Forking lacme-accountd, child PID $pid\n" if $OPTS{debug}; + $ACCOUNTD = $pid; + $s->close() or die "Can't close: $!"; + push @CLEANUP, sub() { + print STDERR "[$$] Shutting down lacme-accountd\n" if $OPTS{debug}; + shutdown($client, SHUT_RDWR) or warn "shutdown: $!"; + kill 15 => $pid; + waitpid $pid => 0; + }; } - print STDERR "[$$] Forking lacme-accountd, child PID $pid\n" if $OPTS{debug}; - $s->close() or die "Can't close: $!"; - push @CLEANUP, sub() { - print STDERR "[$$] Shutting down lacme-accountd\n" if $OPTS{debug}; - shutdown($client, SHUT_RDWR) or warn "shutdown: $!"; - kill 15 => $pid; - waitpid $pid => 0; - }; } else { my @stat; -- cgit v1.2.3