diff --git a/README.md b/README.md
index bf2e5fc46147a99065e3055bcff0bf1469c9dfc6..60a196130f1dceb5058117ca247ed830be1ac772 100644
--- a/README.md
+++ b/README.md
@@ -219,7 +219,7 @@ Supported options:
 - `signing_key`: string. Path to the signing ssh private key you created earlier. See the [note](#a-note-on-files) on files above.
 - `additional_principals`: array of string. By default certificates will have one principal set - the username portion of the requester's email address. If `additional_principals` is set, these will be added to the certificate e.g. if your production machines use shared user accounts.
 - `max_age`: string. If set the server will not issue certificates with an expiration value longer than this, regardless of what the client requests. Must be a valid Go [`time.Duration`](https://golang.org/pkg/time/#ParseDuration) string.
-- `permissions`: array of string. Actions the certificate can perform. See the [`-O` option to `ssh-keygen(1)`](http://man.openbsd.org/OpenBSD-current/man1/ssh-keygen.1) for a complete list.
+- `permissions`: array of string. Specify the actions the certificate can perform. See the [`-O` option to `ssh-keygen(1)`](http://man.openbsd.org/OpenBSD-current/man1/ssh-keygen.1) for a complete list. e.g. `permissions = ["permit-pty", "permit-port-forwarding", force-command=/bin/ls", "source-address=192.168.0.0/24"]`
 
 ## aws
 AWS configuration is only needed for accessing signing keys stored on S3, and isn't totally necessary even then.  
diff --git a/example-server.conf b/example-server.conf
index 9a20c9d0209c93126eb95e79fe30ca278b199f8d..8d299fa72504e7d73689036892adac3d79df3d17 100644
--- a/example-server.conf
+++ b/example-server.conf
@@ -29,7 +29,7 @@ ssh {
   signing_key = "signing_key"  # Path to the CA signing secret key
   additional_principals = ["ec2-user", "ubuntu"]  # Additional principals to allow
   max_age = "720h"  # Maximum lifetime of a ssh certificate
-  permissions = ["permit-pty", "permit-X11-forwarding", "permit-agent-forwarding", "permit-port-forwarding", "permit-user-rc"]  #  Permissions associated with a certificate
+  permissions = ["permit-pty", "permit-X11-forwarding", "permit-agent-forwarding", "permit-port-forwarding", "permit-user-rc", "force-command=/bin/ls"]  #  Permissions associated with a certificate
 }
 
 # Optional AWS config. if an aws config is present, then files (e.g. signing key or tls cert) can be read from S3 using the syntax `/s3/bucket/path/to/signing.key`.
diff --git a/server/signer/signer.go b/server/signer/signer.go
index a4cf919cea5eedd97c673e786433f5b1f50719d8..2a15849d6e409dea9a4c85cb799ca8ee04e5575a 100644
--- a/server/signer/signer.go
+++ b/server/signer/signer.go
@@ -4,6 +4,7 @@ import (
 	"crypto/rand"
 	"fmt"
 	"log"
+	"strings"
 	"time"
 
 	"go4.org/wkfs"
@@ -16,12 +17,38 @@ import (
 	"golang.org/x/crypto/ssh"
 )
 
+var (
+	defaultPermissions = map[string]string{
+		"permit-X11-forwarding":   "",
+		"permit-agent-forwarding": "",
+		"permit-port-forwarding":  "",
+		"permit-pty":              "",
+		"permit-user-rc":          "",
+	}
+)
+
 // KeySigner does the work of signing a ssh public key with the CA key.
 type KeySigner struct {
 	ca          ssh.Signer
 	validity    time.Duration
 	principals  []string
-	permissions map[string]string
+	permissions []string
+}
+
+func (s *KeySigner) setPermissions(cert *ssh.Certificate) {
+	cert.CriticalOptions = make(map[string]string)
+	cert.Extensions = make(map[string]string)
+	for _, perm := range s.permissions {
+		if strings.Contains(perm, "=") {
+			opt := strings.Split(perm, "=")
+			cert.CriticalOptions[strings.TrimSpace(opt[0])] = strings.TrimSpace(opt[1])
+		} else {
+			cert.Extensions[perm] = ""
+		}
+	}
+	if len(cert.Extensions) == 0 {
+		cert.Extensions = defaultPermissions
+	}
 }
 
 // SignUserKey returns a signed ssh certificate.
@@ -35,15 +62,15 @@ func (s *KeySigner) SignUserKey(req *lib.SignRequest, username string) (*ssh.Cer
 		req.ValidUntil = expires
 	}
 	cert := &ssh.Certificate{
-		CertType:    ssh.UserCert,
-		Key:         pubkey,
-		KeyId:       fmt.Sprintf("%s_%d", username, time.Now().UTC().Unix()),
-		ValidBefore: uint64(req.ValidUntil.Unix()),
-		ValidAfter:  uint64(time.Now().UTC().Add(-5 * time.Minute).Unix()),
+		CertType:        ssh.UserCert,
+		Key:             pubkey,
+		KeyId:           fmt.Sprintf("%s_%d", username, time.Now().UTC().Unix()),
+		ValidAfter:      uint64(time.Now().UTC().Add(-5 * time.Minute).Unix()),
+		ValidBefore:     uint64(req.ValidUntil.Unix()),
+		ValidPrincipals: []string{username},
 	}
-	cert.ValidPrincipals = append(cert.ValidPrincipals, username)
 	cert.ValidPrincipals = append(cert.ValidPrincipals, s.principals...)
-	cert.Extensions = s.permissions
+	s.setPermissions(cert)
 	if err := cert.SignCert(rand.Reader, s.ca); err != nil {
 		return nil, err
 	}
@@ -67,23 +94,6 @@ func (s *KeySigner) GenerateRevocationList(certs []*store.CertRecord) ([]byte, e
 	return k.Marshal(rand.Reader)
 }
 
-func makeperms(perms []string) map[string]string {
-	if len(perms) > 0 {
-		m := make(map[string]string)
-		for _, p := range perms {
-			m[p] = ""
-		}
-		return m
-	}
-	return map[string]string{
-		"permit-X11-forwarding":   "",
-		"permit-agent-forwarding": "",
-		"permit-port-forwarding":  "",
-		"permit-pty":              "",
-		"permit-user-rc":          "",
-	}
-}
-
 // New creates a new KeySigner from the supplied configuration.
 func New(conf *config.SSH) (*KeySigner, error) {
 	data, err := wkfs.ReadFile(conf.SigningKey)
@@ -102,6 +112,6 @@ func New(conf *config.SSH) (*KeySigner, error) {
 		ca:          key,
 		validity:    validity,
 		principals:  conf.AdditionalPrincipals,
-		permissions: makeperms(conf.Permissions),
+		permissions: conf.Permissions,
 	}, nil
 }
diff --git a/server/signer/signer_test.go b/server/signer/signer_test.go
index baf00e54142dc73866e2f3a30730ae9d6604eea4..3bbdbf949a2f2f77e80deb503121df423f0dd66c 100644
--- a/server/signer/signer_test.go
+++ b/server/signer/signer_test.go
@@ -17,9 +17,10 @@ import (
 var (
 	key, _ = ssh.ParsePrivateKey(testdata.Priv)
 	signer = &KeySigner{
-		ca:         key,
-		validity:   12 * time.Hour,
-		principals: []string{"ec2-user"},
+		ca:          key,
+		validity:    12 * time.Hour,
+		principals:  []string{"ec2-user"},
+		permissions: []string{"permit-pty", "force-command=/bin/ls"},
 	}
 )
 
@@ -79,3 +80,28 @@ func TestRevocationList(t *testing.T) {
 		t.Errorf("cert %s should not be revoked", cert2.KeyId)
 	}
 }
+
+func TestPermissions(t *testing.T) {
+	t.Parallel()
+	r := &lib.SignRequest{
+		Key:        string(testdata.Pub),
+		ValidUntil: time.Now().Add(1 * time.Hour),
+	}
+	cert, err := signer.SignUserKey(r, "gopher1")
+	if err != nil {
+		t.Error(err)
+	}
+	want := struct {
+		extensions map[string]string
+		options    map[string]string
+	}{
+		extensions: map[string]string{"permit-pty": ""},
+		options:    map[string]string{"force-command": "/bin/ls"},
+	}
+	if !reflect.DeepEqual(cert.Extensions, want.extensions) {
+		t.Errorf("Wrong permissions: wanted: %v got :%v", cert.Extensions, want.extensions)
+	}
+	if !reflect.DeepEqual(cert.CriticalOptions, want.options) {
+		t.Errorf("Wrong options: wanted: %v got :%v", cert.CriticalOptions, want.options)
+	}
+}