App-Acmeman

 view release on metacpan or  search on metacpan

lib/App/Acmeman.pm  view on Meta::CPAN

	    } elsif ($hours == 24) {
		$in = "in one day";
	    } else {
		$in = "today";
	    }
	    debug(2, "$crt expires on $expiry, $in");
	    if ($now + $self->cf->get(qw(core time-delta)) < $ts) {
		return 0;
	    } else {
		debug(2, "will renew $crt (expires on $expiry, $in)");
	    }
	} else {
	    debug(2, "will renew $crt");
	}
    }
    return 1;
}

sub debug_to_loglevel {
    my $self = shift;
    my @lev = ('err', 'info', 'debug');
    my $v = $self->cf->core->verbose;
    return $lev[$v > $#lev ? $#lev : $v];
}

my @challenge_files;

END {
    if (@challenge_files) {
	debug(3, "removing challenge files");
	my $n = unlink @challenge_files;
	unless ($n == @challenge_files) {
	    error("some challenge files were not removed",
		  prefix => 'warning');
	}
    }
}

sub save_challenge {
    my ($self,$challenge) = @_;
    my $file = File::Spec->catfile($self->cf->get(qw(core rootdir)), $challenge->get_path);
    if (open(my $fh, '>', $file)) {
	print $fh $self->acme->make_key_authorization($challenge);
	close $fh;
	debug(3, "wrote challenge file $file");
	push @challenge_files, $file;
    } else {
	error("can't open $file for writing: $!");
	die;
    }
}   

sub account_key {
    my $self = shift;

    unless ($self->{_account_key}) {
	my $keyfile = $self->cf->get('account', 'key');
	if (-r $keyfile) {
	    if (open(my $fh, '<', $keyfile)) {
		local $/ = undef;
		$self->{_account_key} = Crypt::OpenSSL::RSA->new_private_key(<$fh>);
		close $fh;
	    } else {
		error("can't open $keyfile for reading: $!");
	    }
	} else {
	    $self->{_account_key} = Crypt::OpenSSL::RSA->generate_key($self->cf->get('core', 'key-size'));
	}
    }
    return $self->{_account_key};
}

sub account_key_id {
    my $self = shift;
    
    my $idfile = $self->cf->get('account', 'id');
    if (my $val = shift) {
	$self->{_account_key_id} = $val;
	$self->prep_dir($idfile);
	if (open(my $fh, '>', $idfile)) {
	    print $fh $val;
	    close $fh;
	} else {
	    error("can't open $idfile for writing: $!");
	}
    } elsif (!$self->{_account_key_id}) {
	if (-r $idfile) {
	    if (open(my $fh, '<', $idfile)) {
		chomp($self->{_account_key_id} = <$fh>);
		close $fh;
		debug(3, "using key_id $self->{_account_key_id}");
	    } else {
		error("can't open $idfile for reading: $!");
	    }
	}
    }
    return $self->{_account_key_id};
}

sub acme {
    my $self = shift;
    unless ($self->{_acme}) {
	my $acme = Net::ACME2::LetsEncrypt->new(
	    environment => $self->acme_host,
	    key => $self->account_key->get_private_key_string(),
	    key_id => $self->account_key_id
        );
	$self->{_acme} = $acme;

	unless ($acme->key_id()) {
	    # Create new account
	    debug(3, "creating account");
	    my $terms_url = $acme->get_terms_of_service();
	    $acme->create_account(termsOfServiceAgreed => 1);
	    debug(3, "saving account credentials");
	    $self->account_key_id($acme->key_id());
	    my $keyfile = $self->cf->get('account', 'key');
	    if (open(my $fh, '>', $keyfile)) {
	        print $fh $self->account_key->get_private_key_string();
	        close $fh;
	    } else {
		error("can't open $keyfile for writing: $!");
	    }
	}
    }
    return $self->{_acme};
}

sub register_domain_certificate {
    my ($self,$domain) = @_;
    
    my $key_size = $self->cf->get('domain', $domain, 'key-size')
	              || $self->cf->get('core', 'key-size');

    if ($self->cf->core->verbose > 0) {
	my $crt = $domain->certificate_file;
	my $alt = join(',', $domain->alt);
	if (-f $crt) {
	    debug(1, "renewing $crt: CN=$domain, alternatives=$alt, key_size=$key_size");
	} else {
	    debug(1, "issuing $crt: CN=$domain, alternatives=$alt, key_size=$key_size");
	}
    }

    return 1 if $self->dry_run_option;

    my $acme = $self->acme;
	
    # Create order
    my $order = $acme->create_order(
                  identifiers => [
                     map { { type => 'dns', value => $_ } } $domain->names
                  ]
    );
    debug(3, "$domain: created order");

    foreach my $authz (map { $acme->get_authorization($_) } $order->authorizations()) {
    
	my ($challenge) = grep { $_->type() eq 'http-01' } $authz->challenges();
	if (!$challenge) {
	    error("$domain: no challenge of acceptable type received");
	    return 0;
	}

	debug(3, "$domain: serving challenge");
	$self->save_challenge($challenge);
	$acme->accept_challenge($challenge);

	my $ret;
	while (($ret = $acme->poll_authorization($authz)) eq 'pending') {
	    sleep 1
	}
	if ($ret ne 'valid') {
	    my $text = "authorization $ret";
	    if (my ($ch) = grep { $_->type() eq 'http-01' } $authz->challenges()) {
		if (my $err = $ch->error()) {
		    $text .= ': ' . $err->to_string;
		}
	    }

 view all matches for this distribution
 view release on metacpan -  search on metacpan

( run in 0.927 second using v1.00-cache-2.02-grep-82fe00e-cpan-dad7e4baca0 )