Skip to content

Commit

Permalink
Merge pull request #2 from mlcdf/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
mlcdf authored Dec 10, 2022
2 parents f5d0cba + fbed144 commit c9d02bb
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 84 deletions.
9 changes: 1 addition & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ PHP) websites.

- It requires at least a Pro plan (for SSH access).
- The underlying file system is made invisible: deploying a website with a domain www.example.com will upload the content to a www.example.com folder. This is by design and it can't be overridden.
- A few operations are asynchronous and create a task on OVHcloud infrastructure that will be picked up by robots and executed. Therefore, some operations may take several seconds or more.
- When attaching a domain to a hosting, you'll have to wait ~1h for the Let's Encrypt SSL certificates.

# Usage

Expand All @@ -28,7 +26,7 @@ Available commands are:
logs View access logs
open Open browser to current deployed website
remove Remove websites (files & attached domains)
tasks Lists tasks
tasks List tasks
tool Group useful extra-commands
users Manage users
whoami Show info about the user currently logged in
Expand Down Expand Up @@ -57,11 +55,6 @@ Force `go test` to run all the tests (by disabling caching)
./scrits/test.sh
```

## Tools

- check domains
- ci

## License

[MIT](https://choosealicense.com/licenses/mit/)
4 changes: 4 additions & 0 deletions internal/cmdutil/cmdutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ func Highlight(str string) string {
func Special(str string) string {
return lipgloss.NewStyle().Foreground(StyleSpecial).Render(str)
}

func Bold(str string) string {
return lipgloss.NewStyle().Bold(true).Render(str)
}
2 changes: 1 addition & 1 deletion internal/command/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (c *RemoveCommand) Help() string {
helpText := `
Usage: owh remove, rm [<options>]
Remove websites (files & attached domains).
Removes websites (files & attached domains).
Options:
--hosting service name (if not set, you'll be prompt)
Expand Down
4 changes: 2 additions & 2 deletions internal/command/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ func (c *TasksCommand) Help() string {
helpText := `
Usage: owh tasks
Lists tasks
Lists tasks.
`
return strings.TrimSpace(helpText)
}

func (c *TasksCommand) Synopsis() string {
return "Lists tasks"
return "List tasks"
}

func (c *TasksCommand) Run(args []string) int {
Expand Down
240 changes: 240 additions & 0 deletions internal/command/tool_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
package command

import (
"crypto/tls"
"flag"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
"sync"
"text/tabwriter"

"go.mlcdf.fr/owh/internal/api"
"go.mlcdf.fr/owh/internal/cmdutil"
"go.mlcdf.fr/owh/internal/config"
)

type CheckCommand struct {
App
}

func (c *CheckCommand) Help() string {
helpText := `
Usage: owh tool check
Performs various check on your website such as:
- check DNS config
- validate SSL certs
- test http => https redirection
- etc
`
return strings.TrimSpace(helpText)
}

func (c *CheckCommand) Synopsis() string {
return "Perform various check on your website"
}

func (c *CheckCommand) Run(args []string) int {
var hosting string
var domain string

flags := flag.NewFlagSet("link", flag.ExitOnError)

flags.StringVar(&domain, "domain", "", "")

if err := flags.Parse(args); err != nil {
return c.View.PrintErr(err)
}

if domain == "" {
link, err := c.EnsureLink()
if err != nil && err != config.ErrFolderNotLinked {
return c.View.PrintErr(err)
}

domain = link.CanonicalDomain
hosting = link.Hosting
}

client, err := c.LoggedClient()
if err != nil {
return c.View.PrintErr(err)
}

if hosting == "" {
hosting, err = client.HostingByDomain(domain)
if err != nil {
return c.View.PrintErr(err)
}
}

ch := &check{wg: new(sync.WaitGroup), httpClient: c.HTTPClient}
ch.wg.Add(5)

go ch.checkEnforceHTTPS(domain)
go ch.checkValidCert(domain)
go ch.checkProtocol(domain)
go ch.checCustom404(domain)

hostingInfo, err := client.GetHosting(hosting)
if err != nil {
return c.View.PrintErr(err)
}

go ch.checkIP(hostingInfo, domain)

ch.wg.Wait()

w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
defer w.Flush()

fmt.Fprintf(w, cmdutil.Bold("DNS")+"\t\t\n")
fmt.Fprintf(w, " Record A \t=\t%s\n", ch.ipv4)
fmt.Fprintf(w, " Record AAAA \t=\t%s\n", ch.ipv6)
fmt.Fprintf(w, "\t\t\n")
fmt.Fprintf(w, cmdutil.Bold("HTTP")+"\t\t\n")

fmt.Fprintf(w, " Protocol\t=\t%s\n", ch.protocol)
fmt.Fprintf(w, " Valid certificate\t=\t%s\n", ch.validCert)
fmt.Fprintf(w, " Enforce HTTPS\t=\t%s\n", ch.enforceHTTPS)
fmt.Fprintf(w, " Custom 404 page\t=\t%s\n", ch.custom404)

return 0
}

type check struct {
wg *sync.WaitGroup
httpClient *http.Client

ipv4 string
ipv6 string

protocol string
validCert string
enforceHTTPS string
custom404 string
}

func (c *check) checkIP(hosting *api.HostingInfo, domain string) {
defer c.wg.Done()

ips, err := net.LookupIP(domain)
if err != nil {
c.ipv4 = "failed to lookup IP"
c.ipv6 = "failed to lookup IP"
return
}

var ipv4 bool
var ipv6 bool

for _, ip := range ips {
_ip := ip.To4()

if _ip != nil {
if _ip.String() == hosting.HostingIP {
ipv4 = true
}
} else {
if ip.To16().String() == hosting.HostingIPv6 {
ipv6 = true
}
}
}

c.ipv4 = yesno(ipv4)
c.ipv6 = yesno(ipv6)
}

func yesno(value bool) string {
if value {
return "yes"
}

return "no"
}

func (c *check) checkValidCert(domain string) {
defer c.wg.Done()

conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", domain), nil)
if err == nil {
err = conn.VerifyHostname(domain)
if err != nil {
c.validCert = fmt.Sprintf("no, hostname doesn't match: %s", err)
}

c.validCert = "yes"
return
}

c.validCert = fmt.Sprintf("no, %s", err)
}

func (c *check) checkEnforceHTTPS(domain string) {
defer c.wg.Done()

url := fmt.Sprintf("http://%s", domain)

noRedirectClient := c.httpClient
noRedirectClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { return nil }

res, err := noRedirectClient.Get(url)
if err != nil {
c.enforceHTTPS = err.Error()
return
}

res.Body.Close()

if (res.StatusCode == 301 || res.StatusCode == 302) &&
strings.Contains(res.Header.Get("location"), fmt.Sprintf("https://%s", domain)) {
c.enforceHTTPS = "yes"
return
}

c.enforceHTTPS = "no"
}

func (c *check) checkProtocol(domain string) {
defer c.wg.Done()

res, err := http.Get("https://" + domain)
if err != nil {
c.protocol = err.Error()
return
}
res.Body.Close()

c.protocol = res.Proto
}

func (c *check) checCustom404(domain string) {
defer c.wg.Done()

res, err := c.httpClient.Get(fmt.Sprintf("https://%s/thispagedoesnotexists", domain))
if err != nil {
c.custom404 = err.Error()
return
}

defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
c.custom404 = err.Error()
return
}

if strings.Contains(string(body), "<title>404 Not Found</title>") &&
strings.Contains(string(body), "<p>The requested URL was not found on this server.</p>") {
c.custom404 = "no"
return
}

c.custom404 = "yes"
}
4 changes: 2 additions & 2 deletions internal/command/tool_ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ func (c *CICommand) Help() string {
helpText := `
Usage: owh tool ci
Help you setup a deployment in CI.
Helps you setup a deployment in CI.
/!\ This command will display secrets in the terminal.
`
return strings.TrimSpace(helpText)
}

func (c *CICommand) Synopsis() string {
return "Shows useful info to setup a CI"
return "Show useful info to setup a CI"
}

func (c *CICommand) Run(args []string) int {
Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ func main() {
"tool": func() (cli.Command, error) {
return &command.ToolCommand{App: *app}, nil
},
"tool check": func() (cli.Command, error) {
return &command.CheckCommand{App: *app}, nil
},
"tool ci": func() (cli.Command, error) {
return &command.CICommand{App: *app}, nil
},
Expand Down
12 changes: 0 additions & 12 deletions tests/test-project/index.html

This file was deleted.

13 changes: 0 additions & 13 deletions tests/venom/0_init.yml

This file was deleted.

19 changes: 0 additions & 19 deletions tests/venom/auth.yml

This file was deleted.

10 changes: 0 additions & 10 deletions tests/venom/deploy.yml

This file was deleted.

Loading

0 comments on commit c9d02bb

Please sign in to comment.