diff --git a/src/builder.rs b/src/builder.rs index fddb7fa..aa385bc 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,11 +1,12 @@ use crate::command_runner::{SetuidCommandRunner, StdCommandRunner}; use crate::resources::{ - AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeUser, Cert, + AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeRootCert, AcmeUser, Cert, CertChain, Cron, Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle, LoadedDirectory, MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase, Resource, ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory, SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation, }; +use crate::static_files::LETS_ENCRYPT_R3; use crate::storage::SimpleStorage; use crate::storage::Storage; use crate::symbols::acme::Cert as CertSymbol; @@ -98,6 +99,7 @@ impl ImplementationBuilder> for DefaultBuilder { impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = ( Csr, + AcmeRootCert, AcmeAccountKey, AcmeChallengesDir, AcmeUser, @@ -106,6 +108,7 @@ impl ImplementationBuilder> for DefaultBuilder { fn prerequisites(resource: &Cert) -> Self::Prerequisites { ( Csr(resource.0.clone()), + AcmeRootCert, AcmeAccountKey, AcmeChallengesDir, AcmeUser, @@ -118,11 +121,12 @@ impl ImplementationBuilder> for DefaultBuilder { fn create( resource: &Cert, target: & as Resource>::Artifact, - (csr, account_key, challenges_dir, (user_name, _), _): ::Artifact, + (csr, root_cert, account_key, challenges_dir, (user_name, _), _): ::Artifact, ) -> Self::Implementation { CertSymbol::new( resource.0.clone(), SetuidCommandRunner::new(user_name.0), + root_cert.clone_rc(), account_key.clone_rc(), challenges_dir.clone_rc(), csr.clone_rc(), @@ -132,18 +136,18 @@ impl ImplementationBuilder> for DefaultBuilder { } impl ImplementationBuilder> for DefaultBuilder { - type Prerequisites = Cert; + type Prerequisites = (Cert, AcmeRootCert); fn prerequisites(resource: &CertChain) -> Self::Prerequisites { - Cert(resource.0.clone()) + (Cert(resource.0.clone()), AcmeRootCert) } - type Implementation = (); + type Implementation = ConcatSymbol<[Rc; 2], Rc, Rc>; fn create( _resource: &CertChain, - _target: & as Resource>::Artifact, - _cert: ::Artifact, + target: & as Resource>::Artifact, + (cert, root_cert): ::Artifact, ) -> Self::Implementation { - () + ConcatSymbol::new([cert.clone_rc(), root_cert.clone_rc()], target.clone_rc()) } } @@ -663,6 +667,20 @@ impl ImplementationBuilder for DefaultBuilder { } } +impl ImplementationBuilder for DefaultBuilder { + type Prerequisites = (); + fn prerequisites(_resource: &AcmeRootCert) -> Self::Prerequisites {} + + type Implementation = FileSymbol, &'static str>; + fn create( + _resource: &AcmeRootCert, + target: &::Artifact, + (): ::Artifact, + ) -> Self::Implementation { + FileSymbol::new(target.clone_rc(), LETS_ENCRYPT_R3) + } +} + impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &MariaDbUser) -> Self::Prerequisites {} diff --git a/src/locator.rs b/src/locator.rs index 277566b..01a6e41 100644 --- a/src/locator.rs +++ b/src/locator.rs @@ -3,7 +3,7 @@ use crate::artifacts::{ UserName as UserNameArtifact, }; use crate::resources::{ - AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeUser, Cert, + AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeRootCert, AcmeUser, Cert, CertChain, Cron, Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle, LoadedDirectory, MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase, Resource, ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory, @@ -90,11 +90,17 @@ impl> ResourceLocator> for DefaultLocator

{ } impl> ResourceLocator> for DefaultLocator

{ - type Prerequisites = >>::Prerequisites; + type Prerequisites = Dir>; fn locate( resource: &CertChain, ) -> ( as Resource>::Artifact, Self::Prerequisites) { - >>::locate(&Cert(&resource.0)) + ( + PathArtifact::from(format!( + "/etc/ssl/local_certs/{}.chained.crt", + resource.0.as_ref() + )), + Dir::new("/etc/ssl/local_certs"), + ) } } @@ -231,6 +237,20 @@ impl ResourceLocator for DefaultLocator

ResourceLocator for DefaultLocator

{ + type Prerequisites = Dir>; + fn locate( + _resource: &AcmeRootCert, + ) -> (::Artifact, Self::Prerequisites) { + let acme_user = P::acme_user(); + let home = P::user_home(acme_user); + ( + PathArtifact::from(home.join("lets_encrypt_r3.pem")), + Dir(home), + ) + } +} + impl> ResourceLocator> for DefaultLocator

{ type Prerequisites = (); fn locate( diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 06daa5f..8c56a67 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -94,6 +94,12 @@ impl Resource for AcmeUser { type Artifact = (UserNameArtifact, PathArtifact); } +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct AcmeRootCert; +impl Resource for AcmeRootCert { + type Artifact = PathArtifact; +} + #[derive(Debug, Hash, PartialEq, Eq)] pub struct AcmeChallengesDir; impl Resource for AcmeChallengesDir { @@ -342,6 +348,7 @@ default_resources!( AcmeAccountKey: AcmeAccountKey, AcmeChallengesDir: AcmeChallengesDir, AcmeChallengesNginxSnippet: AcmeChallengesNginxSnippet, + AcmeRootCert: AcmeRootCert, AcmeUser: AcmeUser, Cert: Cert, CertChain: CertChain, diff --git a/src/symbols/acme/cert.rs b/src/symbols/acme/cert.rs index 955606b..9055c85 100644 --- a/src/symbols/acme/cert.rs +++ b/src/symbols/acme/cert.rs @@ -12,6 +12,7 @@ use std::path::Path; pub struct Cert<_C, C, D, P> { domain: D, command_runner: C, + root_cert_path: P, account_key_path: P, challenges_path: P, csr_path: P, @@ -23,6 +24,7 @@ impl<_C, C, D, P> Cert<_C, C, D, P> { pub fn new( domain: D, command_runner: C, + root_cert_path: P, account_key_path: P, challenges_path: P, csr_path: P, @@ -31,6 +33,7 @@ impl<_C, C, D, P> Cert<_C, C, D, P> { Self { domain, command_runner, + root_cert_path, account_key_path, challenges_path, csr_path, @@ -81,12 +84,8 @@ impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef, P: AsRef> Symbol for "openssl", args![ "verify", - // Since the cert file includes the intermediate, - // this pulls the intermediate into the verification chain - // without trusting it - "-untrusted", - self.cert_path.as_ref(), - // Only the first cert in the cert file is verified + "--untrusted", + self.root_cert_path.as_ref(), self.cert_path.as_ref(), ], ) diff --git a/static_files/lets_encrypt_r3.pem b/static_files/lets_encrypt_r3.pem new file mode 100644 index 0000000..43b222a --- /dev/null +++ b/static_files/lets_encrypt_r3.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw +WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP +R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx +sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm +NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg +Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG +/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA +FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw +AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw +Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB +gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W +PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl +ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz +CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm +lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 +avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 +yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O +yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids +hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ +HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv +MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX +nLRbwHOoq7hHwg== +-----END CERTIFICATE-----