Example #1
0
File: shell.go Project: itpkg/chaos
//Shell shell commands
func (p *Engine) Shell() []cli.Command {
	return []cli.Command{
		{
			Name:    "nginx",
			Aliases: []string{"ng"},
			Usage:   "init nginx config file",
			Action: web.Action(func(*cli.Context) error {
				const tpl = `
upstream {{.Domain}}_prod {
  server localhost:{{.Port}} fail_timeout=0;
}

server {
  listen {{if .Ssl}}443{{- else}}80{{- end}};

{{if .Ssl}}
  ssl  on;
  ssl_certificate  ssl/{{.Domain}}-cert.pem;
  ssl_certificate_key  ssl/{{.Domain}}-key.pem;
  ssl_session_timeout  5m;
  ssl_protocols  SSLv2 SSLv3 TLSv1;
  ssl_ciphers  RC4:HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers  on;
{{- end}}

  client_max_body_size 4G;
  keepalive_timeout 10;

  proxy_buffers 16 64k;
  proxy_buffer_size 128k;

  server_name {{.Domain}};

  root {{.Root}}/public;
  index index.html;

  access_log /var/log/nginx/{{.Domain}}.access.log;
  error_log /var/log/nginx/{{.Domain}}.error.log;

  location / {
    try_files $uri $uri/ /index.html?/$request_uri;
  }

#  location ^~ /assets/ {
#    gzip_static on;
#    expires max;
#    access_log off;
#    add_header Cache-Control "public";
#  }

  location ~* \.(?:css|js)$ {
    gzip_static on;
    expires max;
    access_log off;
    add_header Cache-Control "public";
  }
  location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
    expires 1M;
    access_log off;
    add_header Cache-Control "public";
  }

  location ~* \.(?:rss|atom)$ {
    expires 12h;
    access_log off;
    add_header Cache-Control "public";
  }

  location ~ ^/api/{{.Version}}(/?)(.*) {
    {{if .Ssl}}proxy_set_header X-Forwarded-Proto https;{{- end}}
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_redirect off;
    proxy_pass http://{{.Domain}}_prod/$2$is_args$args;
    # limit_req zone=one;
  }

}

`
				t, err := template.New("").Parse(tpl)
				if err != nil {
					return err
				}
				pwd, err := os.Getwd()
				if err != nil {
					return err
				}

				domain := viper.GetString("http.domain")
				fn := path.Join("etc", "nginx", "sites-enabled", domain+".conf")
				if err = os.MkdirAll(path.Dir(fn), 0700); err != nil {
					return err
				}
				fmt.Printf("generate file %s\n", fn)
				fd, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
				if err != nil {
					return err
				}
				defer fd.Close()

				return t.Execute(fd, struct {
					Domain  string
					Port    int
					Ssl     bool
					Root    string
					Version string
				}{
					Ssl:     viper.GetBool("http.ssl"),
					Domain:  domain,
					Port:    viper.GetInt("http.port"),
					Root:    pwd,
					Version: "v1",
				})
			}),
		},
		{
			Name:    "redis",
			Aliases: []string{"re"},
			Usage:   "open redis connection",
			Action: web.Action(func(*cli.Context) error {
				return Shell(
					"redis-cli",
					"-h", viper.GetString("redis.host"),
					"-p", viper.GetString("redis.port"),
					"-n", viper.GetString("redis.db"),
				)
			}),
		},
		{
			Name:    "database",
			Aliases: []string{"db"},
			Usage:   "database operations",
			Subcommands: []cli.Command{
				{
					Name:    "example",
					Usage:   "scripts example for create database and user",
					Aliases: []string{"e"},
					Action: web.Action(func(*cli.Context) error {
						drv := viper.GetString("database.driver")
						args := viper.GetStringMapString("database.args")
						var err error
						switch drv {
						case "postgres":
							fmt.Printf("CREATE USER %s WITH PASSWORD '%s';\n", args["user"], args["password"])
							fmt.Printf("CREATE DATABASE %s WITH ENCODING='UTF8';\n", args["dbname"])
							fmt.Printf("GRANT ALL PRIVILEGES ON DATABASE %s TO %s;\n", args["dbname"], args["user"])
						default:
							err = fmt.Errorf("unknown driver %s", drv)
						}
						return err
					}),
				},
				{
					Name:    "migrate",
					Usage:   "migrate the database",
					Aliases: []string{"m"},
					Action: web.Action(func(*cli.Context) error {
						db, err := web.OpenDatabase()
						if err != nil {
							return err
						}
						return web.Loop(func(en web.Engine) error {
							en.Migrate(db)
							return nil
						})
					}),
				},
				{
					Name:    "seed",
					Usage:   "load the seed data",
					Aliases: []string{"s"},
					Action: web.IocAction(func(*cli.Context, *inject.Graph) error {
						return web.Loop(func(en web.Engine) error {
							en.Seed()
							return nil
						})
					}),
				},
				{
					Name:    "connect",
					Usage:   "connect database",
					Aliases: []string{"c"},
					Action: web.Action(func(*cli.Context) error {
						drv := viper.GetString("database.driver")
						args := viper.GetStringMapString("database.args")
						var err error
						switch drv {
						case "postgres":
							err = Shell("psql",
								"-h", args["host"],
								"-p", args["port"],
								"-U", args["user"],
								args["dbname"],
							)
						default:
							err = fmt.Errorf("unknown driver %s", drv)
						}
						return err
					}),
				},
				{
					Name:    "create",
					Usage:   "create database",
					Aliases: []string{"n"},
					Action: web.Action(func(*cli.Context) error {
						drv := viper.GetString("database.driver")
						args := viper.GetStringMapString("database.args")
						var err error
						switch drv {
						case "postgres":
							err = Shell("psql",
								"-h", args["host"],
								"-p", args["port"],
								"-U", args["user"],
								"-c", fmt.Sprintf("create database %s WITH ENCODING='UTF8'", args["dbname"]),
							)
						default:
							err = fmt.Errorf("unknown driver %s", drv)
						}
						return err
					}),
				},
				{
					Name:    "drop",
					Usage:   "drop database",
					Aliases: []string{"d"},
					Action: web.Action(func(*cli.Context) error {
						drv := viper.GetString("database.driver")
						args := viper.GetStringMapString("database.args")
						var err error
						switch drv {
						case "postgres":
							err = Shell("psql",
								"-h", args["host"],
								"-p", args["port"],
								"-U", args["user"],
								"-c", fmt.Sprintf("drop database %s", args["dbname"]),
							)
						default:
							err = fmt.Errorf("unknown driver %s", drv)
						}
						return err
					}),
				},
			},
		},
		{
			Name:    "cache",
			Aliases: []string{"c"},
			Usage:   "cache operations",
			Subcommands: []cli.Command{
				{
					Name:    "list",
					Usage:   "list all cache keys",
					Aliases: []string{"l"},
					Action: web.IocAction(func(*cli.Context, *inject.Graph) error {
						keys, err := p.Cache.Keys()
						if err != nil {
							return err
						}
						for _, k := range keys {
							fmt.Println(k)
						}
						return nil
					}),
				},
				{
					Name:    "clear",
					Usage:   "clear cache items",
					Aliases: []string{"c"},
					Action: web.IocAction(func(*cli.Context, *inject.Graph) error {
						return p.Cache.Flush()
					}),
				},
			},
		},
		{
			Name:    "oauth",
			Aliases: []string{"oa"},
			Usage:   "oauth credentials.",
			Subcommands: []cli.Command{
				{
					Name:    "google",
					Usage:   "google oauth v2",
					Aliases: []string{"g"},
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "file, f",
							Value: "",
							Usage: "google oauth2 json filename.",
						},
					},
					Action: web.IocAction(func(c *cli.Context, g *inject.Graph) error {
						fn := c.String("file")
						if fn == "" {
							return errors.New("filename mustn't empty")
						}

						fd, err := os.Open(fn)
						if err != nil {
							return err
						}
						defer fd.Close()

						var gc GoogleCredential
						dec := json.NewDecoder(fd)
						if err := dec.Decode(&gc); err != nil {
							return err
						}
						return p.Dao.Set("google.oauth", gc.To(), true)
					}),
				},
			},
		},
		{
			Name:    "users",
			Aliases: []string{"us"},
			Usage:   "users manager",
			Subcommands: []cli.Command{
				{
					Name:    "list",
					Usage:   "list all users",
					Aliases: []string{"l"},
					Action: web.IocAction(func(*cli.Context, *inject.Graph) error {
						var us []User
						if err := p.Dao.Db.
							Select([]string{"uid", "name", "email"}).
							Order("last_sign_in DESC").
							Find(&us).Error; err != nil {
							return err
						}
						fmt.Println("UID\tUSER")
						for _, u := range us {
							fmt.Printf("%s\t%s<%s>\n", u.UID, u.Name, u.Email)
						}
						return nil
					}),
				},
				{
					Name:    "role",
					Usage:   "add/remove user's role",
					Aliases: []string{"r"},
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:  "uid, u",
							Value: "",
							Usage: "user's uid",
						},
						cli.StringFlag{
							Name:  "name, n",
							Value: "",
							Usage: "role's name",
						},
						cli.BoolFlag{
							Name:  "deny, d",
							Usage: "remove role from user",
						},
						cli.IntFlag{
							Name:  "years, y",
							Value: 10,
							Usage: "years",
						},
					},
					Action: web.IocAction(func(c *cli.Context, _ *inject.Graph) error {
						uid := c.String("uid")
						name := c.String("name")
						deny := c.Bool("deny")
						years := c.Int("years")
						if uid == "" {
							return errors.New("uid mustn't empty")
						}
						if name == "" {
							return errors.New("role's name mustn't empty")
						}
						user, err := p.Dao.GetUser(uid)
						if err != nil {
							return err
						}
						role, err := p.Dao.Role(name, "-", 0)
						if err != nil {
							return err
						}
						if deny {
							err = p.Dao.Deny(role.ID, user.ID)
						} else {
							err = p.Dao.Allow(role.ID, user.ID, years, 0, 0)
						}
						return err
					}),
				},
			},
		},
	}
}
Example #2
0
File: shell.go Project: itpkg/chaos
//Shell command
func (p *Engine) Shell() []cli.Command {
	return []cli.Command{
		{
			Name:    "ops:mail",
			Aliases: []string{"ops:m"},
			Usage:   "mail server manage",
			Subcommands: []cli.Command{
				{
					Name:    "postfix",
					Aliases: []string{"pf"},
					Flags:   []cli.Flag{ops.FlagDatabaseUser, ops.FlagDatabasePassword},
					Usage:   "generate postfix config files",
					Action: web.Action(func(c *cli.Context) error {
						files := map[string]string{
							"etc/postfix/main.cf": `
# See /usr/share/postfix/main.cf.dist for a commented, more complete version

# Debian specific:  Specifying a file name will cause the first
# line of that file to be used as the name.  The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname

smtpd_banner = $myhostname ESMTP $mail_name (IT-PACKAGE)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

readme_directory = no

smtpd_tls_cert_file=/etc/dovecot/dovecot.pem
smtpd_tls_key_file=/etc/dovecot/private/dovecot.pem
smtpd_use_tls=yes
smtpd_tls_auth_only = yes

#Enabling SMTP for authenticated users, and handing off authentication to Dovecot
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

smtpd_recipient_restrictions =
        permit_sasl_authenticated,
        permit_mynetworks,
        reject_unauth_destination

# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.

myhostname = CHANGE-ME
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all

#Handing off local delivery to Dovecot's LMTP, and telling it where to store mail
virtual_transport = lmtp:unix:private/dovecot-lmtp

#Virtual domains, users, and aliases
virtual_mailbox_domains = {{.Driver}}:/etc/postfix/virtual-mailbox-domains.cf
virtual_mailbox_maps = {{.Driver}}:/etc/postfix/virtual-mailbox-maps.cf
virtual_alias_maps = {{.Driver}}:/etc/postfix/virtual-alias-maps.cf,
  {{.Driver}}:/etc/postfix/virtual-email2email.cf
							`,
							"etc/postfix/virtual-mailbox-domains.cf": `
user = {{.User}}
password = {{.Password}}
hosts = {{.Host}}
dbname = {{.Name}}
query = SELECT 1 FROM {{.DomainTable}} WHERE name='%s'
							`,
							"etc/postfix/virtual-mailbox-maps.cf": `
user = {{.User}}
password = {{.Password}}
hosts = {{.Host}}
dbname = {{.Name}}
query = SELECT 1 FROM {{.UserTable}} WHERE email='%s'
							`,
							"etc/postfix/virtual-alias-maps.cf": `
user = {{.User}}
password = {{.Password}}
hosts = {{.Host}}
dbname = {{.Name}}
query = SELECT destination FROM {{.AliasTable}} WHERE source='%s'
							`,
							"etc/postfix/virtual-email2email.cf": `
user = {{.User}}
password = {{.Password}}
hosts = {{.Host}}
dbname = {{.Name}}
query = SELECT email FROM {{.UserTable}} WHERE email='%s'
							`,
							"etc/postfix/master.cf": `
#
# Postfix master process configuration file.  For details on the format
# of the file, see the master(5) manual page (command: "man 5 master").
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       -       -       -       smtpd
#smtp      inet  n       -       -       -       1       postscreen
#smtpd     pass  -       -       -       -       -       smtpd
#dnsblog   unix  -       -       -       -       0       dnsblog
#tlsproxy  unix  -       -       -       -       0       tlsproxy
submission inet n       -       -       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
smtps     inet  n       -       -       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
							`,
							"tmp/postfix.sh": `
#!/bin/sh
postmap -q example.com {{.Driver}}:/etc/postfix/virtual-mailbox-domains.cf
postmap -q [email protected] {{.Driver}}:/etc/postfix/virtual-mailbox-maps.cf
postmap -q [email protected] {{.Driver}}:/etc/postfix/virtual-alias-maps.cf

							`,
						}
						return write_config(c, files)
					}),
				},
				{
					Name:    "database",
					Aliases: []string{"db"},
					Flags:   []cli.Flag{ops.FlagDatabaseUser, ops.FlagDatabasePassword},
					Usage:   "create readonly user to database",
					Action: web.Action(func(c *cli.Context) error {
						files := map[string]string{
							"tmp/mail.sql": `
CREATE USER {{.User}} PASSWORD '{{.Password}}';
GRANT SELECT ON {{.UserTable}}, {{.DomainTable}}, {{.AliasTable}} TO {{.User}};
							`,
						}
						return write_config(c, files)
					}),
				},
				{
					Name:    "dovecot",
					Aliases: []string{"dv"},
					Flags:   []cli.Flag{ops.FlagDatabaseUser, ops.FlagDatabasePassword},
					Usage:   "generate dovecot config files",
					Action: web.Action(func(c *cli.Context) error {
						files := map[string]string{
							"etc/dovecot/dovecot.conf": `
## Dovecot configuration file

# If you're in a hurry, see http://wiki2.dovecot.org/QuickConfiguration

# "doveconf -n" command gives a clean output of the changed settings. Use it
# instead of copy&pasting files when posting to the Dovecot mailing list.

# '#' character and everything after it is treated as comments. Extra spaces
# and tabs are ignored. If you want to use either of these explicitly, put the
# value inside quotes, eg.: key = "# char and trailing whitespace  "

# Default values are shown for each setting, it's not required to uncomment
# those. These are exceptions to this though: No sections (e.g. namespace {})
# or plugin settings are added by default, they're listed only as examples.
# Paths are also just examples with the real defaults being based on configure
# options. The paths listed here are for configure --prefix=/usr
# --sysconfdir=/etc --localstatedir=/var

# Enable installed protocols
!include_try /usr/share/dovecot/protocols.d/*.protocol
protocols = imap lmtp

# A comma separated list of IPs or hosts where to listen in for connections.
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
# If you want to specify non-default ports or anything more complex,
# edit conf.d/master.conf.
#listen = *, ::

# Base directory where to store runtime data.
#base_dir = /var/run/dovecot/

# Name of this instance. Used to prefix all Dovecot processes in ps output.
#instance_name = dovecot

# Greeting message for clients.
#login_greeting = Dovecot ready.

# Space separated list of trusted network ranges. Connections from these
# IPs are allowed to override their IP addresses and ports (for logging and
# for authentication checks). disable_plaintext_auth is also ignored for
# these networks. Typically you'd specify the IMAP proxy servers here.
#login_trusted_networks =

# Sepace separated list of login access check sockets (e.g. tcpwrap)
#login_access_sockets =

# Show more verbose process titles (in ps). Currently shows user name and
# IP address. Useful for seeing who are actually using the IMAP processes
# (eg. shared mailboxes or if same uid is used for multiple accounts).
#verbose_proctitle = no

# Should all processes be killed when Dovecot master process shuts down.
# Setting this to "no" means that Dovecot can be upgraded without
# forcing existing client connections to close (although that could also be
# a problem if the upgrade is e.g. because of a security fix).
#shutdown_clients = yes

# If non-zero, run mail commands via this many connections to doveadm server,
# instead of running them directly in the same process.
#doveadm_worker_count = 0
# UNIX socket or host:port used for connecting to doveadm server
#doveadm_socket_path = doveadm-server

# Space separated list of environment variables that are preserved on Dovecot
# startup and passed down to all of its child processes. You can also give
# key=value pairs to always set specific settings.
#import_environment = TZ

##
## Dictionary server settings
##

# Dictionary can be used to store key=value lists. This is used by several
# plugins. The dictionary can be accessed either directly or though a
# dictionary server. The following dict block maps dictionary names to URIs
# when the server is used. These can then be referenced using URIs in format
# "proxy::<name>".

dict {
  #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
  #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext
}

# Most of the actual configuration gets included below. The filenames are
# first sorted by their ASCII value and parsed in that order. The 00-prefixes
# in filenames are intended to make it easier to understand the ordering.
!include conf.d/*.conf

# A config file can also tried to be included without giving an error if
# it's not found:
!include_try local.conf
							`,
							"etc/dovecot/conf.d/10-mail.conf": `
mail_location = maildir:/var/mail/vhosts/%d/%n
mail_privileged_group = mail
							`,
							"etc/dovecot/conf.d/10-auth.conf": `
disable_plaintext_auth = yes
auth_mechanisms = plain login

#!include auth-system.conf.ext
!include auth-sql.conf.ext
							`,
							"etc/dovecot/conf.d/auth-sql.conf.ext": `
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}
							`,
							"etc/dovecot/dovecot-sql.conf.ext": `
driver = {{.Driver}}
connect = host={{.Host}} dbname={{.Name}} user={{.User}} password={{.Password}}
default_pass_scheme = SHA512-CRYPT
#password_query = SELECT email as user, password FROM {{.UserTable}} WHERE email='%u';
password_query = SELECT email as user, password FROM {{.UserTable}} WHERE email=(SELECT destination FROM {{.AliasTable}} WHERE source = '%u');
							`,
							"etc/dovecot/conf.d/10-master.conf": `
service auth-worker {
  # Auth worker process is run as root by default, so that it can access
  # /etc/shadow. If this isn't necessary, the user should be changed to
  # $default_internal_user.
  user = vmail
}

service imap-login {
	inet_listener imap {
	  #port = 0
	}
	inet_listener imaps {
		port = 993
		ssl = yes
	}
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
   }
    # Create inet listener only if you can't use the above UNIX socket
    #inet_listener lmtp {
      # Avoid making LMTP visible for the entire internet
      #address =
      #port =
    #}
  }
}

service auth {
  # auth_socket_path points to this userdb socket by default. It's typically
  # used by dovecot-lda, doveadm, possibly imap process, etc. Its default
  # permissions make it readable only by root, but you may need to relax these
  # permissions. Users that have access to this socket are able to get a list
  # of all usernames and get results of everyone's userdb lookups.
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }

  unix_listener auth-userdb {
    mode = 0600
    user = vmail
    #group =
  }

  # Postfix smtp-auth
  #unix_listener /var/spool/postfix/private/auth {
  #  mode = 0666
  #}

  # Auth process is run as this user.
  user = dovecot
}
							`,
							"etc/dovecot/conf.d/10-ssl.conf": `
ssl_cert = </etc/dovecot/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.pem

ssl = required
							`,
							"tmp/dovecot.sh": `
#!/bin/sh
mkdir -p /var/mail/vhosts
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /var/mail
passwd -l vmail
chown -R vmail:vmail /var/mail
chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot
							`,
						}
						return write_config(c, files)
					}),
				},
			},
		},
	}
}