diff --git a/Dockerfile b/Dockerfile index e4d864a..f6bc620 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,8 @@ COPY ./test_data/apache/example2.com.conf ${apacheAvailableSitesDir} COPY ./test_data/apache/example-ssl.com.conf ${apacheAvailableSitesDir} COPY ./test_data/apache/example3.com.conf ${apacheAvailableSitesDir} COPY ./test_data/apache/example3-ssl.com.conf ${apacheAvailableSitesDir} -RUN a2ensite example3.com.conf && a2ensite example3-ssl.com.conf && a2ensite example2.com.conf && a2ensite example-ssl.com.conf && a2dissite 000-default.conf +COPY ./test_data/apache/example4-ssl.com.conf ${apacheAvailableSitesDir} +COPY ./test_data/apache/example5.com.conf ${apacheAvailableSitesDir} +RUN a2ensite example3.com.conf && a2ensite example3-ssl.com.conf && a2ensite example2.com.conf && a2ensite example-ssl.com.conf && a2ensite example4-ssl.com.conf && a2ensite example5.com.conf && a2dissite 000-default.conf ENTRYPOINT ["/bin/sh", "./testcmd.sh"] diff --git a/configurator.go b/configurator.go index 2e02bb8..f255198 100644 --- a/configurator.go +++ b/configurator.go @@ -31,8 +31,8 @@ type ApacheConfigurator interface { PrepareHTTPSModules(temp bool) error EnableModule(module string, temp bool) error EnsurePortIsListening(port string, https bool) error - GetSuitableVhost(serverName string, createIfNoSsl bool) (*entity.VirtualHost, error) - FindSuitableVhost(serverName string) (*entity.VirtualHost, error) + GetSuitableVhosts(serverName string, createIfNoSsl bool) ([]*entity.VirtualHost, error) + FindSuitableVhosts(serverName string) ([]*entity.VirtualHost, error) CheckConfiguration() bool RestartWebServer() error SetLogger(logger logger.Logger) @@ -41,15 +41,14 @@ type ApacheConfigurator interface { } type apacheConfigurator struct { - parser *Parser - reverter *Reverter - ctl *apache.Ctl - site *apache.Site - logger logger.Logger - version string - vhosts []*entity.VirtualHost - suitableVhosts map[string]*entity.VirtualHost - options map[string]string + parser *Parser + reverter *Reverter + ctl *apache.Ctl + site *apache.Site + logger logger.Logger + version string + vhosts []*entity.VirtualHost + options map[string]string } type vhsotNames struct { @@ -178,9 +177,9 @@ func (ac *apacheConfigurator) Rollback() error { // DeployCertificate installs certificate to a domain func (ac *apacheConfigurator) DeployCertificate(serverName, certPath, certKeyPath, chainPath, fullChainPath string) error { var err error - var vhost *entity.VirtualHost + var vhosts []*entity.VirtualHost - if vhost, err = ac.GetSuitableVhost(serverName, true); err != nil { + if vhosts, err = ac.GetSuitableVhosts(serverName, true); err != nil { return err } @@ -192,60 +191,63 @@ func (ac *apacheConfigurator) DeployCertificate(serverName, certPath, certKeyPat return errors.New("could not find ssl_module") } - if err = ac.addDummySSLDirectives(vhost.AugPath); err != nil { - return err - } - - if err = ac.cleanSSLVhost(vhost); err != nil { - return err - } - - augCertPath, err := ac.parser.FindDirective("SSLCertificateFile", "", vhost.AugPath, true) - if err != nil { - return fmt.Errorf("error while searching directive 'SSLCertificateFile': %v", err) - } + for _, vhost := range vhosts { + if err = ac.cleanSSLVhost(vhost); err != nil { + return err + } - augCertKeyPath, err := ac.parser.FindDirective("SSLCertificateKeyFile", "", vhost.AugPath, true) - if err != nil { - return fmt.Errorf("error while searching directive 'SSLCertificateKeyFile': %v", err) - } + if err = ac.addDummySSLDirectives(vhost.AugPath); err != nil { + return err + } - res, err := utils.CheckMinVersion(ac.version, "2.4.8") - if err != nil { - return err - } + augCertPath, err := ac.parser.FindDirective("SSLCertificateFile", "", vhost.AugPath, true) + if err != nil { + return fmt.Errorf("error while searching directive 'SSLCertificateFile': %v", err) + } - if !res || (chainPath != "" && fullChainPath == "") { - if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], certPath); err != nil { - return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err) + augCertKeyPath, err := ac.parser.FindDirective("SSLCertificateKeyFile", "", vhost.AugPath, true) + if err != nil { + return fmt.Errorf("error while searching directive 'SSLCertificateKeyFile': %v", err) } - if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil { - return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err) + + res, err := utils.CheckMinVersion(ac.version, "2.4.8") + if err != nil { + return err } - if chainPath != "" { - if err = ac.parser.AddDirective(vhost.AugPath, "SSLCertificateChainFile", []string{chainPath}); err != nil { - return fmt.Errorf("could not add 'SSLCertificateChainFile' directive to vhost '%s': %v", serverName, err) + if !res || (chainPath != "" && fullChainPath == "") { + if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], certPath); err != nil { + return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err) + } + + if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil { + return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err) + } + + if chainPath != "" { + if err = ac.parser.AddDirective(vhost.AugPath, "SSLCertificateChainFile", []string{chainPath}); err != nil { + return fmt.Errorf("could not add 'SSLCertificateChainFile' directive to vhost '%s': %v", serverName, err) + } + } else { + return fmt.Errorf("SSL certificate chain path is required for the current Apache version '%s', but is not specified", ac.version) } } else { - return fmt.Errorf("SSL certificate chain path is required for the current Apache version '%s', but is not specified", ac.version) - } - } else { - if fullChainPath == "" { - return errors.New("SSL certificate fullchain path is required, but is not specified") - } + if fullChainPath == "" { + return errors.New("SSL certificate fullchain path is required, but is not specified") + } - if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], fullChainPath); err != nil { - return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err) - } - if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil { - return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err) + if err = ac.parser.Augeas.Set(augCertPath[len(augCertPath)-1], fullChainPath); err != nil { + return fmt.Errorf("could not set certificate path for vhost '%s': %v", serverName, err) + } + if err = ac.parser.Augeas.Set(augCertKeyPath[len(augCertKeyPath)-1], certKeyPath); err != nil { + return fmt.Errorf("could not set certificate key path for vhost '%s': %v", serverName, err) + } } - } - if !vhost.Enabled { - if err = ac.EnableSite(vhost); err != nil { - return err + if !vhost.Enabled { + if err = ac.EnableSite(vhost); err != nil { + return err + } } } @@ -414,38 +416,13 @@ func (ac *apacheConfigurator) addDummySSLDirectives(vhPath string) error { } func (ac *apacheConfigurator) cleanSSLVhost(vhost *entity.VirtualHost) error { - if err := ac.deduplicateDirectives(vhost.AugPath, []string{"SSLEngine", "SSLCertificateFile", "SSLCertificateKeyFile"}); err != nil { - return err - } - - if err := ac.removeDirectives(vhost.AugPath, []string{"SSLCertificateChainFile"}); err != nil { + if err := ac.removeDirectives(vhost.AugPath, []string{"SSLEngine", "SSLCertificateFile", "SSLCertificateKeyFile", "SSLCertificateChainFile"}); err != nil { return err } return nil } -func (ac *apacheConfigurator) deduplicateDirectives(vhPath string, directives []string) error { - for _, directive := range directives { - directivePaths, err := ac.parser.FindDirective(directive, "", vhPath, false) - - if err != nil { - return err - } - - reg := regexp.MustCompile(`/\w*$`) - - if len(directivePaths) > 1 { - dps := directivePaths[:len(directivePaths)-1] - for _, dp := range dps { - ac.parser.Augeas.Remove(reg.ReplaceAllString(dp, "")) - } - } - } - - return nil -} - func (ac *apacheConfigurator) removeDirectives(vhPath string, directives []string) error { for _, directive := range directives { directivePaths, err := ac.parser.FindDirective(directive, "", vhPath, false) @@ -509,50 +486,36 @@ func (ac *apacheConfigurator) addListensForHTTPS(listens []string, listensOrigin return nil } -// GetSuitableVhost returns suitable virtual hosts for provided serverName. +// GetSuitableVhosts returns suitable virtual hosts for provided serverName. // If createIfNoSsl is true then ssl part will be created if neccessary. -func (ac *apacheConfigurator) GetSuitableVhost(serverName string, createIfNoSsl bool) (*entity.VirtualHost, error) { - if vhost, ok := ac.suitableVhosts[serverName]; ok { - return vhost, nil - } - - vhost, err := ac.FindSuitableVhost(serverName) - +func (ac *apacheConfigurator) GetSuitableVhosts(serverName string, createIfNoSsl bool) ([]*entity.VirtualHost, error) { + var suitableVhosts []*entity.VirtualHost + suitableVhosts, err := ac.FindSuitableVhosts(serverName) if err != nil { return nil, err } - if vhost == nil { - return nil, fmt.Errorf("could not find suitable virtual host with ServerName: %s", serverName) + if len(suitableVhosts) == 0 { + return nil, fmt.Errorf("could not find suitable virtual hosts with ServerName: %s", serverName) } if !createIfNoSsl { - return vhost, nil - } - - if !vhost.Ssl { - serverName := vhost.ServerName - vhost, err = ac.makeVhostSsl(vhost) - - if err != nil { - return nil, fmt.Errorf("could not create ssl virtual host for '%s': %v", serverName, err) - } + return suitableVhosts, nil } - ac.suitableVhosts[serverName] = vhost - - return vhost, nil + return ac.makeSslVhosts(suitableVhosts) } -// FindSuitableVhost tries to find a suitable virtual host for provided serverName. -func (ac *apacheConfigurator) FindSuitableVhost(serverName string) (*entity.VirtualHost, error) { +// FindSuitableVhosts tries to find a suitable virtual host for provided serverName. +func (ac *apacheConfigurator) FindSuitableVhosts(serverName string) ([]*entity.VirtualHost, error) { vhosts, err := ac.GetVhosts() - if err != nil { return nil, err } - var suitableVhost *entity.VirtualHost + var suitableVhosts []*entity.VirtualHost + var suitableNonSslVhosts []*entity.VirtualHost + var sslVostsAddresses []string for _, vhost := range vhosts { if vhost.ModMacro { @@ -561,79 +524,95 @@ func (ac *apacheConfigurator) FindSuitableVhost(serverName string) (*entity.Virt } // Prefer virtual host with ssl - if vhost.ServerName == serverName && vhost.Ssl { - return vhost, nil - } - if vhost.ServerName == serverName { - suitableVhost = vhost + if vhost.Ssl { + suitableVhosts = append(suitableVhosts, vhost) + sslVostsAddresses = append(sslVostsAddresses, vhost.GetAddressesString(true)) + } else { + suitableNonSslVhosts = append(suitableNonSslVhosts, vhost) + } } } - return suitableVhost, nil -} - -// makeVhostSsl makes an ssl virtual host version of a nonssl virtual host -func (ac *apacheConfigurator) makeVhostSsl(noSslVhost *entity.VirtualHost) (*entity.VirtualHost, error) { - noSslFilePath := noSslVhost.FilePath - sslFilePath, err := ac.getSslVhostFilePath(noSslFilePath) - - if err != nil { - return nil, fmt.Errorf("could not get config file path for ssl virtual host: %v", err) + for _, vhost := range suitableNonSslVhosts { + // skip non ssl vhosts if there is already ssl vhost with the same address + if !com.IsSliceContainsStr(sslVostsAddresses, vhost.GetAddressesString(true)) { + suitableVhosts = append(suitableVhosts, vhost) + } } - originMatches, err := ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath))) + return suitableVhosts, nil +} - if err != nil { - return nil, err - } +// makeSslVhosts makes an ssl virtual host version of a nonssl virtual host +func (ac *apacheConfigurator) makeSslVhosts(vhosts []*entity.VirtualHost) ([]*entity.VirtualHost, error) { + var totalVhosts []*entity.VirtualHost + var newSslVhosts []*entity.VirtualHost + var newMatches []string - err = ac.copyCreateSslVhostSkeleton(noSslVhost, sslFilePath) + for _, vhost := range vhosts { + if vhost.Ssl { + totalVhosts = append(totalVhosts, vhost) + continue + } - if err != nil { - return nil, fmt.Errorf("could not create config for ssl virtual host: %v", err) - } + noSslFilePath := vhost.FilePath + sslFilePath, err := ac.getSslVhostFilePath(noSslFilePath) - ac.parser.Augeas.Load() - newMatches, err := ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath))) + if err != nil { + return nil, fmt.Errorf("could not get config file path for ssl virtual host: %v", err) + } - if err != nil { - return nil, err - } + originMatches, err := ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath))) + if err != nil { + return nil, err + } - sslVhostPath := getNewVhostPathFromAugesMatches(originMatches, newMatches) + if err = ac.copyCreateSslVhostSkeleton(vhost, sslFilePath); err != nil { + return nil, fmt.Errorf("could not create config for ssl virtual host: %v", err) + } - if sslVhostPath == "" { + // Reload augeas to take into account the new vhost + ac.parser.Augeas.Load() newMatches, err = ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath))) if err != nil { return nil, err } - sslVhostPath = getNewVhostPathFromAugesMatches(originMatches, newMatches) - + sslVhostPath := getNewVhostPathFromAugesMatches(originMatches, newMatches) if sslVhostPath == "" { - return nil, errors.New("could not reverse map the HTTPS VirtualHost to the original") - } - } + newMatches, err = ac.parser.Augeas.Match(fmt.Sprintf("/files%s//*[label()=~regexp('VirtualHost', 'i')]", escape(sslFilePath))) - ac.updateSslVhostAddresses(sslVhostPath) - err = ac.Save() + if err != nil { + return nil, err + } - if err != nil { - return nil, err - } + sslVhostPath = getNewVhostPathFromAugesMatches(originMatches, newMatches) - sslVhost, err := ac.createVhost(sslVhostPath) + if sslVhostPath == "" { + return nil, errors.New("could not reverse map the HTTPS VirtualHost to the original") + } + } - if err != nil { - return nil, err + ac.updateSslVhostAddresses(sslVhostPath) + if err := ac.Save(); err != nil { + return nil, err + } + + sslVhost, err := ac.createVhost(sslVhostPath) + if err != nil { + return nil, err + } + + sslVhost.Ancestor = vhost + newSslVhosts = append(newSslVhosts, sslVhost) } - sslVhost.Ancestor = noSslVhost - ac.vhosts = append(ac.vhosts, sslVhost) + updateVhostsAugPath(newSslVhosts, newMatches) + totalVhosts = append(totalVhosts, newSslVhosts...) - return sslVhost, nil + return totalVhosts, nil } // CheckConfiguration checks if apache configuration is correct @@ -864,7 +843,7 @@ func (ac *apacheConfigurator) createVhost(path string) (*entity.VirtualHost, err var macro bool - if strings.Index(strings.ToLower(path), "/macro/") != -1 { + if strings.Contains(strings.ToLower(path), "/macro/") { macro = true } @@ -1007,14 +986,13 @@ func GetApacheConfigurator(options map[string]string) (ApacheConfigurator, error } configurator := apacheConfigurator{ - parser: parser, - reverter: &Reverter{apacheSite: apache.GetApacheSite(options), logger: &log}, - ctl: ctl, - site: &apache.Site{}, - logger: &log, - options: options, - version: version, - suitableVhosts: make(map[string]*entity.VirtualHost), + parser: parser, + reverter: &Reverter{apacheSite: apache.GetApacheSite(options), logger: &log}, + ctl: ctl, + site: &apache.Site{}, + logger: &log, + options: options, + version: version, } return &configurator, nil @@ -1184,3 +1162,15 @@ func getNewVhostPathFromAugesMatches(originMatches []string, newMatches []string return "" } + +func updateVhostsAugPath(vhosts []*entity.VirtualHost, newMatches []string) { + for _, newMatch := range newMatches { + mNewMatch := strings.Replace(newMatch, "[1]", "", -1) + + for _, vhost := range vhosts { + if vhost.AugPath == mNewMatch { + vhost.AugPath = newMatch + } + } + } +} diff --git a/configurator_test.go b/configurator_test.go index a4d9eea..414dc94 100644 --- a/configurator_test.go +++ b/configurator_test.go @@ -273,14 +273,14 @@ func TestGetVhosts(t *testing.T) { func TestFindSuitableVhost(t *testing.T) { configurator := getConfigurator(t) - vhost := getVhost(t, configurator, "example2.com") - assert.Equal(t, "example2.com", vhost.ServerName) + vhosts := getVhosts(t, configurator, "example2.com") + assert.Equal(t, "example2.com", vhosts[0].ServerName) } func TestGetVhostBlockContent(t *testing.T) { configurator := getConfigurator(t) - vhost := getVhost(t, configurator, "example2.com") - content, err := configurator.getVhostBlockContent(vhost) + vhosts := getVhosts(t, configurator, "example2.com") + content, err := configurator.getVhostBlockContent(vhosts[0]) assert.Nilf(t, err, "could not get vhost block content: %v", err) expectedContent := getVhostConfigContent(t, "example2.com.conf") expectedContent = prepareStringToCompare(expectedContent) @@ -326,7 +326,7 @@ func TestEnsurePortIsListening(t *testing.T) { } } -func TestGetSuitableVhost(t *testing.T) { +func TestGetSuitableVhostsSingle(t *testing.T) { type vhostItem struct { serverName, sslConfigFilePath, docRoot string ssl, enabled bool @@ -339,8 +339,10 @@ func TestGetSuitableVhost(t *testing.T) { } for _, vhostItem := range vhostItems { - sslVhost, err := configurator.GetSuitableVhost(vhostItem.serverName, true) + sslVhosts, err := configurator.GetSuitableVhosts(vhostItem.serverName, true) assert.Nilf(t, err, "could not get ssl vhost: %v", err) + assert.Equal(t, 1, len(sslVhosts)) + sslVhost := sslVhosts[0] assert.Equal(t, vhostItem.sslConfigFilePath, sslVhost.FilePath) // Check that ssl config file realy exists assert.Equal(t, true, com.IsFile(vhostItem.sslConfigFilePath)) @@ -361,6 +363,51 @@ func TestGetSuitableVhost(t *testing.T) { } } +func TestGetSuitableVhostsMultiple(t *testing.T) { + configurator := getConfigurator(t) + sslVhosts, err := configurator.GetSuitableVhosts("example4.com", true) + assert.Nilf(t, err, "could not get ssl vhost: %v", err) + assert.Equal(t, 2, len(sslVhosts)) + addresses := []string{"[2002:5bcc:18fd:c:10:52:43:96]", "10.52.43.96"} + + for _, sslVhost := range sslVhosts { + assert.Equal(t, "/etc/apache2/sites-enabled/example4-ssl.com.conf", sslVhost.FilePath) + // Check that ssl config file realy exists + assert.Equal(t, true, com.IsFile("/etc/apache2/sites-enabled/example4-ssl.com.conf")) + assert.Equal(t, "example4.com", sslVhost.ServerName) + assert.Equal(t, "/var/www/html", sslVhost.DocRoot) + assert.Equal(t, true, sslVhost.Ssl) + assert.Equal(t, true, sslVhost.Enabled) + assert.Equal(t, false, sslVhost.ModMacro) + assert.Equal(t, 1, len(sslVhost.Addresses)) + assert.Equal(t, true, com.IsSliceContainsStr(addresses, sslVhost.GetAddressesString(true))) + } +} + +func TestDeployCertificate(t *testing.T) { + configurator := getConfigurator(t) + err := configurator.DeployCertificate("example5.com", "/opt/a2conf/test_data/apache/certificate/example.com.crt", "/opt/a2conf/test_data/apache/certificate/example.com.key", "", "/opt/a2conf/test_data/apache/certificate/example.com.crt") + assert.Nilf(t, err, "could not deploy certificate to vhost: %v", err) + err = configurator.Save() + assert.Nilf(t, err, "could not save changes after certificate deploy: %v", err) + assert.Equal(t, true, configurator.CheckConfiguration()) + err = configurator.Commit() + assert.Nilf(t, err, "could not commit changes after certificate deploy: %v", err) + err = configurator.RestartWebServer() + assert.Nilf(t, err, "could not restart webserver after certificate deploy: %v", err) + // Check that ssl config file realy exists + sslConfigFilePath := "/etc/apache2/sites-enabled/example5.com-ssl.conf" + assert.Equal(t, true, com.IsFile(sslConfigFilePath)) + + sslConfigContent, err := ioutil.ReadFile(sslConfigFilePath) + assert.Nilf(t, err, "could not read apache vhost ssl config file '%s' content: %v", sslConfigFilePath, err) + directives := []string{"SSLCertificateKeyFile /opt/a2conf/test_data/apache/certificate/example.com.key", "SSLEngine on", "SSLCertificateFile /opt/a2conf/test_data/apache/certificate/example.com.crt"} + + for _, directive := range directives { + assert.Containsf(t, string(sslConfigContent), directive, "ssl config does not contain directive '%s'", directive) + } +} + func getVhostsJSON(t *testing.T) string { vhostsPath := apacheDir + "/vhosts.json" assert.FileExists(t, vhostsPath, "could not open vhosts file") @@ -377,12 +424,12 @@ func getConfigurator(t *testing.T) *apacheConfigurator { return configurator.(*apacheConfigurator) } -func getVhost(t *testing.T, configurator ApacheConfigurator, serverName string) *entity.VirtualHost { - vhost, err := configurator.FindSuitableVhost(serverName) +func getVhosts(t *testing.T, configurator ApacheConfigurator, serverName string) []*entity.VirtualHost { + vhosts, err := configurator.FindSuitableVhosts(serverName) assert.Nilf(t, err, "could not find suitable vhost: %v", err) - assert.NotNilf(t, vhost, "could not find suitable vhost for '%s' servername", serverName) + assert.NotEmptyf(t, vhosts, "could not find suitable vhost for '%s' servername", serverName) - return vhost + return vhosts } func getVhostConfigContent(t *testing.T, name string) string { diff --git a/entity/virtualhost.go b/entity/virtualhost.go index ecb125e..dfb3291 100644 --- a/entity/virtualhost.go +++ b/entity/virtualhost.go @@ -3,6 +3,7 @@ package entity import ( "path/filepath" "regexp" + "strings" ) const ( @@ -53,3 +54,17 @@ func (vh *VirtualHost) GetNames() ([]string, error) { func (vh *VirtualHost) GetConfigName() string { return filepath.Base(vh.FilePath) } + +// GetAddressesString return address as a string: "172.10.52.2:80 172.10.52.3:8080" +func (vh *VirtualHost) GetAddressesString(hostsOnly bool) string { + var addresses []string + for _, address := range vh.Addresses { + if hostsOnly { + addresses = append(addresses, address.Host) + } else { + addresses = append(addresses, address.ToString()) + } + } + + return strings.Join(addresses, " ") +} diff --git a/go.sum b/go.sum index b0a75cc..36389c6 100644 --- a/go.sum +++ b/go.sum @@ -14,12 +14,12 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PX github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/parser.go b/parser.go index 171ed80..14c1614 100644 --- a/parser.go +++ b/parser.go @@ -78,13 +78,11 @@ func GetParser(apachectl *apache.Ctl, version, serverRoot, vhostRoot string) (*P if err = parser.setLocations(); err != nil { parser.Close() - return nil, err } if err = parser.ParseFile(parser.ConfigRoot); err != nil { parser.Close() - return nil, fmt.Errorf("could not parse apache config: %v", err) } diff --git a/test_data/apache/example4-ssl.com.conf b/test_data/apache/example4-ssl.com.conf new file mode 100644 index 0000000..a41f702 --- /dev/null +++ b/test_data/apache/example4-ssl.com.conf @@ -0,0 +1,73 @@ + + ServerName example4.com + ServerAlias www.example4.com + DocumentRoot /var/www/html + + + Options Indexes FollowSymlinks + AllowOverride All + Require all granted + + + DirectoryIndex index.html + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + + + ServerName example4.com + ServerAlias www.example4.com + DocumentRoot /var/www/html + + + Options Indexes FollowSymlinks + AllowOverride All + Require all granted + + + DirectoryIndex index.html + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + + + ServerName example4.com + ServerAlias www.example4.com + DocumentRoot /var/www/html + SSLEngine on + SSLCertificateFile /opt/a2conf/test_data/apache/certificate/example.com.crt + SSLCertificateKeyFile /opt/a2conf/test_data/apache/certificate/example.com.key + + + Options Indexes FollowSymlinks + AllowOverride All + Require all granted + + + DirectoryIndex index.html + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + + + ServerName example4.com + ServerAlias www.example4.com + DocumentRoot /var/www/html + SSLEngine on + SSLCertificateFile /opt/a2conf/test_data/apache/certificate/example.com.crt + SSLCertificateKeyFile /opt/a2conf/test_data/apache/certificate/example.com.key + + + Options Indexes FollowSymlinks + AllowOverride All + Require all granted + + + DirectoryIndex index.html + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/test_data/apache/example5.com.conf b/test_data/apache/example5.com.conf new file mode 100644 index 0000000..8989d06 --- /dev/null +++ b/test_data/apache/example5.com.conf @@ -0,0 +1,16 @@ + + ServerName example5.com + ServerAlias www.example5.com + DocumentRoot /var/www/html + + + Options Indexes FollowSymlinks + AllowOverride All + Require all granted + + + DirectoryIndex index.html + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/test_data/apache/vhosts.json b/test_data/apache/vhosts.json index e5827a0..4f764a9 100644 --- a/test_data/apache/vhosts.json +++ b/test_data/apache/vhosts.json @@ -1,4 +1,22 @@ [ + { + "FilePath":"/etc/apache2/sites-enabled/example5.com.conf", + "ServerName":"example5.com", + "DocRoot":"/var/www/html", + "AugPath":"/files/etc/apache2/sites-enabled/example5.com.conf/VirtualHost", + "Addresses":{ + "Kjo4MA==":{ + "IsIpv6":false, + "Host":"*", + "Port": "80" + } + }, + "Aliases":["www.example5.com"], + "Ssl":false, + "Enabled":true, + "ModMacro":false, + "Ancestor":null + }, { "FilePath":"/etc/apache2/sites-enabled/example3.com.conf", "ServerName":"example3.com", @@ -17,6 +35,78 @@ "ModMacro":false, "Ancestor":null }, + { + "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf", + "ServerName":"example4.com", + "DocRoot":"/var/www/html", + "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[1]", + "Addresses":{ + "MTAuNTIuNDMuOTY6ODA=":{ + "IsIpv6":false, + "Host":"10.52.43.96", + "Port": "80" + } + }, + "Aliases":["www.example4.com"], + "Ssl":false, + "Enabled":true, + "ModMacro":false, + "Ancestor":null + }, + { + "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf", + "ServerName":"example4.com", + "DocRoot":"/var/www/html", + "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[2]", + "Addresses":{ + "WzIwMDI6NWJjYzoxOGZkOmM6MTA6NTI6NDM6OTZdOjgw":{ + "IsIpv6":true, + "Host":"[2002:5bcc:18fd:c:10:52:43:96]", + "Port": "80" + } + }, + "Aliases":["www.example4.com"], + "Ssl":false, + "Enabled":true, + "ModMacro":false, + "Ancestor":null + }, + { + "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf", + "ServerName":"example4.com", + "DocRoot":"/var/www/html", + "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[3]", + "Addresses":{ + "MTAuNTIuNDMuOTY6NDQz":{ + "IsIpv6":false, + "Host":"10.52.43.96", + "Port": "443" + } + }, + "Aliases":["www.example4.com"], + "Ssl":true, + "Enabled":true, + "ModMacro":false, + "Ancestor":null + }, + { + "FilePath":"/etc/apache2/sites-enabled/example4-ssl.com.conf", + "ServerName":"example4.com", + "DocRoot":"/var/www/html", + "AugPath":"/files/etc/apache2/sites-enabled/example4-ssl.com.conf/VirtualHost[4]", + "Addresses":{ + "WzIwMDI6NWJjYzoxOGZkOmM6MTA6NTI6NDM6OTZdOjQ0Mw==":{ + "IsIpv6":true, + "Host":"[2002:5bcc:18fd:c:10:52:43:96]", + "Port": "443" + } + }, + "Aliases":["www.example4.com"], + "Ssl":true, + "Enabled":true, + "ModMacro":false, + "Ancestor":null + }, { "FilePath":"/etc/apache2/sites-enabled/example-ssl.com.conf", "ServerName":"example.com", diff --git a/utils/common_test.go b/utils/common_test.go index 76cb7f1..7fe9123 100644 --- a/utils/common_test.go +++ b/utils/common_test.go @@ -35,3 +35,10 @@ func TestCheckMinVersion(t *testing.T) { assert.Nilf(t, err, "failed to check min version %v:", err) assert.Equal(t, false, result) } + +func TestIsCommandExist(t *testing.T) { + result := IsCommandExist("a2ensite") + assert.Equal(t, true, result) + result = IsCommandExist("fakeCommand") + assert.Equal(t, false, result) +}