Browse code

Add encrypt/decrypt helper commands

Jordan Liggitt authored on 2016/02/27 04:29:04
Showing 17 changed files
... ...
@@ -2978,6 +2978,105 @@ _oadm_ca_create-signer-cert()
2978 2978
     must_have_one_noun=()
2979 2979
 }
2980 2980
 
2981
+_oadm_ca_encrypt()
2982
+{
2983
+    last_command="oadm_ca_encrypt"
2984
+    commands=()
2985
+
2986
+    flags=()
2987
+    two_word_flags=()
2988
+    flags_with_completion=()
2989
+    flags_completion=()
2990
+
2991
+    flags+=("--genkey=")
2992
+    flags_with_completion+=("--genkey")
2993
+    flags_completion+=("_filedir")
2994
+    flags+=("--in=")
2995
+    flags_with_completion+=("--in")
2996
+    flags_completion+=("_filedir")
2997
+    flags+=("--key=")
2998
+    flags_with_completion+=("--key")
2999
+    flags_completion+=("_filedir")
3000
+    flags+=("--out=")
3001
+    flags_with_completion+=("--out")
3002
+    flags_completion+=("_filedir")
3003
+    flags+=("--api-version=")
3004
+    flags+=("--certificate-authority=")
3005
+    flags_with_completion+=("--certificate-authority")
3006
+    flags_completion+=("_filedir")
3007
+    flags+=("--client-certificate=")
3008
+    flags_with_completion+=("--client-certificate")
3009
+    flags_completion+=("_filedir")
3010
+    flags+=("--client-key=")
3011
+    flags_with_completion+=("--client-key")
3012
+    flags_completion+=("_filedir")
3013
+    flags+=("--cluster=")
3014
+    flags+=("--config=")
3015
+    flags_with_completion+=("--config")
3016
+    flags_completion+=("_filedir")
3017
+    flags+=("--context=")
3018
+    flags+=("--google-json-key=")
3019
+    flags+=("--insecure-skip-tls-verify")
3020
+    flags+=("--log-flush-frequency=")
3021
+    flags+=("--match-server-version")
3022
+    flags+=("--namespace=")
3023
+    two_word_flags+=("-n")
3024
+    flags+=("--server=")
3025
+    flags+=("--token=")
3026
+    flags+=("--user=")
3027
+
3028
+    must_have_one_flag=()
3029
+    must_have_one_noun=()
3030
+}
3031
+
3032
+_oadm_ca_decrypt()
3033
+{
3034
+    last_command="oadm_ca_decrypt"
3035
+    commands=()
3036
+
3037
+    flags=()
3038
+    two_word_flags=()
3039
+    flags_with_completion=()
3040
+    flags_completion=()
3041
+
3042
+    flags+=("--in=")
3043
+    flags_with_completion+=("--in")
3044
+    flags_completion+=("_filedir")
3045
+    flags+=("--key=")
3046
+    flags_with_completion+=("--key")
3047
+    flags_completion+=("_filedir")
3048
+    flags+=("--out=")
3049
+    flags_with_completion+=("--out")
3050
+    flags_completion+=("_filedir")
3051
+    flags+=("--api-version=")
3052
+    flags+=("--certificate-authority=")
3053
+    flags_with_completion+=("--certificate-authority")
3054
+    flags_completion+=("_filedir")
3055
+    flags+=("--client-certificate=")
3056
+    flags_with_completion+=("--client-certificate")
3057
+    flags_completion+=("_filedir")
3058
+    flags+=("--client-key=")
3059
+    flags_with_completion+=("--client-key")
3060
+    flags_completion+=("_filedir")
3061
+    flags+=("--cluster=")
3062
+    flags+=("--config=")
3063
+    flags_with_completion+=("--config")
3064
+    flags_completion+=("_filedir")
3065
+    flags+=("--context=")
3066
+    flags+=("--google-json-key=")
3067
+    flags+=("--insecure-skip-tls-verify")
3068
+    flags+=("--log-flush-frequency=")
3069
+    flags+=("--match-server-version")
3070
+    flags+=("--namespace=")
3071
+    two_word_flags+=("-n")
3072
+    flags+=("--server=")
3073
+    flags+=("--token=")
3074
+    flags+=("--user=")
3075
+
3076
+    must_have_one_flag=()
3077
+    must_have_one_noun=()
3078
+}
3079
+
2981 3080
 _oadm_ca()
2982 3081
 {
2983 3082
     last_command="oadm_ca"
... ...
@@ -2986,6 +3085,8 @@ _oadm_ca()
2986 2986
     commands+=("create-key-pair")
2987 2987
     commands+=("create-server-cert")
2988 2988
     commands+=("create-signer-cert")
2989
+    commands+=("encrypt")
2990
+    commands+=("decrypt")
2989 2991
 
2990 2992
     flags=()
2991 2993
     two_word_flags=()
... ...
@@ -5714,6 +5714,105 @@ _oc_adm_ca_create-signer-cert()
5714 5714
     must_have_one_noun=()
5715 5715
 }
5716 5716
 
5717
+_oc_adm_ca_encrypt()
5718
+{
5719
+    last_command="oc_adm_ca_encrypt"
5720
+    commands=()
5721
+
5722
+    flags=()
5723
+    two_word_flags=()
5724
+    flags_with_completion=()
5725
+    flags_completion=()
5726
+
5727
+    flags+=("--genkey=")
5728
+    flags_with_completion+=("--genkey")
5729
+    flags_completion+=("_filedir")
5730
+    flags+=("--in=")
5731
+    flags_with_completion+=("--in")
5732
+    flags_completion+=("_filedir")
5733
+    flags+=("--key=")
5734
+    flags_with_completion+=("--key")
5735
+    flags_completion+=("_filedir")
5736
+    flags+=("--out=")
5737
+    flags_with_completion+=("--out")
5738
+    flags_completion+=("_filedir")
5739
+    flags+=("--api-version=")
5740
+    flags+=("--certificate-authority=")
5741
+    flags_with_completion+=("--certificate-authority")
5742
+    flags_completion+=("_filedir")
5743
+    flags+=("--client-certificate=")
5744
+    flags_with_completion+=("--client-certificate")
5745
+    flags_completion+=("_filedir")
5746
+    flags+=("--client-key=")
5747
+    flags_with_completion+=("--client-key")
5748
+    flags_completion+=("_filedir")
5749
+    flags+=("--cluster=")
5750
+    flags+=("--config=")
5751
+    flags_with_completion+=("--config")
5752
+    flags_completion+=("_filedir")
5753
+    flags+=("--context=")
5754
+    flags+=("--google-json-key=")
5755
+    flags+=("--insecure-skip-tls-verify")
5756
+    flags+=("--log-flush-frequency=")
5757
+    flags+=("--match-server-version")
5758
+    flags+=("--namespace=")
5759
+    two_word_flags+=("-n")
5760
+    flags+=("--server=")
5761
+    flags+=("--token=")
5762
+    flags+=("--user=")
5763
+
5764
+    must_have_one_flag=()
5765
+    must_have_one_noun=()
5766
+}
5767
+
5768
+_oc_adm_ca_decrypt()
5769
+{
5770
+    last_command="oc_adm_ca_decrypt"
5771
+    commands=()
5772
+
5773
+    flags=()
5774
+    two_word_flags=()
5775
+    flags_with_completion=()
5776
+    flags_completion=()
5777
+
5778
+    flags+=("--in=")
5779
+    flags_with_completion+=("--in")
5780
+    flags_completion+=("_filedir")
5781
+    flags+=("--key=")
5782
+    flags_with_completion+=("--key")
5783
+    flags_completion+=("_filedir")
5784
+    flags+=("--out=")
5785
+    flags_with_completion+=("--out")
5786
+    flags_completion+=("_filedir")
5787
+    flags+=("--api-version=")
5788
+    flags+=("--certificate-authority=")
5789
+    flags_with_completion+=("--certificate-authority")
5790
+    flags_completion+=("_filedir")
5791
+    flags+=("--client-certificate=")
5792
+    flags_with_completion+=("--client-certificate")
5793
+    flags_completion+=("_filedir")
5794
+    flags+=("--client-key=")
5795
+    flags_with_completion+=("--client-key")
5796
+    flags_completion+=("_filedir")
5797
+    flags+=("--cluster=")
5798
+    flags+=("--config=")
5799
+    flags_with_completion+=("--config")
5800
+    flags_completion+=("_filedir")
5801
+    flags+=("--context=")
5802
+    flags+=("--google-json-key=")
5803
+    flags+=("--insecure-skip-tls-verify")
5804
+    flags+=("--log-flush-frequency=")
5805
+    flags+=("--match-server-version")
5806
+    flags+=("--namespace=")
5807
+    two_word_flags+=("-n")
5808
+    flags+=("--server=")
5809
+    flags+=("--token=")
5810
+    flags+=("--user=")
5811
+
5812
+    must_have_one_flag=()
5813
+    must_have_one_noun=()
5814
+}
5815
+
5717 5816
 _oc_adm_ca()
5718 5817
 {
5719 5818
     last_command="oc_adm_ca"
... ...
@@ -5722,6 +5821,8 @@ _oc_adm_ca()
5722 5722
     commands+=("create-key-pair")
5723 5723
     commands+=("create-server-cert")
5724 5724
     commands+=("create-signer-cert")
5725
+    commands+=("encrypt")
5726
+    commands+=("decrypt")
5725 5727
 
5726 5728
     flags=()
5727 5729
     two_word_flags=()
... ...
@@ -3533,6 +3533,105 @@ _openshift_admin_ca_create-signer-cert()
3533 3533
     must_have_one_noun=()
3534 3534
 }
3535 3535
 
3536
+_openshift_admin_ca_encrypt()
3537
+{
3538
+    last_command="openshift_admin_ca_encrypt"
3539
+    commands=()
3540
+
3541
+    flags=()
3542
+    two_word_flags=()
3543
+    flags_with_completion=()
3544
+    flags_completion=()
3545
+
3546
+    flags+=("--genkey=")
3547
+    flags_with_completion+=("--genkey")
3548
+    flags_completion+=("_filedir")
3549
+    flags+=("--in=")
3550
+    flags_with_completion+=("--in")
3551
+    flags_completion+=("_filedir")
3552
+    flags+=("--key=")
3553
+    flags_with_completion+=("--key")
3554
+    flags_completion+=("_filedir")
3555
+    flags+=("--out=")
3556
+    flags_with_completion+=("--out")
3557
+    flags_completion+=("_filedir")
3558
+    flags+=("--api-version=")
3559
+    flags+=("--certificate-authority=")
3560
+    flags_with_completion+=("--certificate-authority")
3561
+    flags_completion+=("_filedir")
3562
+    flags+=("--client-certificate=")
3563
+    flags_with_completion+=("--client-certificate")
3564
+    flags_completion+=("_filedir")
3565
+    flags+=("--client-key=")
3566
+    flags_with_completion+=("--client-key")
3567
+    flags_completion+=("_filedir")
3568
+    flags+=("--cluster=")
3569
+    flags+=("--config=")
3570
+    flags_with_completion+=("--config")
3571
+    flags_completion+=("_filedir")
3572
+    flags+=("--context=")
3573
+    flags+=("--google-json-key=")
3574
+    flags+=("--insecure-skip-tls-verify")
3575
+    flags+=("--log-flush-frequency=")
3576
+    flags+=("--match-server-version")
3577
+    flags+=("--namespace=")
3578
+    two_word_flags+=("-n")
3579
+    flags+=("--server=")
3580
+    flags+=("--token=")
3581
+    flags+=("--user=")
3582
+
3583
+    must_have_one_flag=()
3584
+    must_have_one_noun=()
3585
+}
3586
+
3587
+_openshift_admin_ca_decrypt()
3588
+{
3589
+    last_command="openshift_admin_ca_decrypt"
3590
+    commands=()
3591
+
3592
+    flags=()
3593
+    two_word_flags=()
3594
+    flags_with_completion=()
3595
+    flags_completion=()
3596
+
3597
+    flags+=("--in=")
3598
+    flags_with_completion+=("--in")
3599
+    flags_completion+=("_filedir")
3600
+    flags+=("--key=")
3601
+    flags_with_completion+=("--key")
3602
+    flags_completion+=("_filedir")
3603
+    flags+=("--out=")
3604
+    flags_with_completion+=("--out")
3605
+    flags_completion+=("_filedir")
3606
+    flags+=("--api-version=")
3607
+    flags+=("--certificate-authority=")
3608
+    flags_with_completion+=("--certificate-authority")
3609
+    flags_completion+=("_filedir")
3610
+    flags+=("--client-certificate=")
3611
+    flags_with_completion+=("--client-certificate")
3612
+    flags_completion+=("_filedir")
3613
+    flags+=("--client-key=")
3614
+    flags_with_completion+=("--client-key")
3615
+    flags_completion+=("_filedir")
3616
+    flags+=("--cluster=")
3617
+    flags+=("--config=")
3618
+    flags_with_completion+=("--config")
3619
+    flags_completion+=("_filedir")
3620
+    flags+=("--context=")
3621
+    flags+=("--google-json-key=")
3622
+    flags+=("--insecure-skip-tls-verify")
3623
+    flags+=("--log-flush-frequency=")
3624
+    flags+=("--match-server-version")
3625
+    flags+=("--namespace=")
3626
+    two_word_flags+=("-n")
3627
+    flags+=("--server=")
3628
+    flags+=("--token=")
3629
+    flags+=("--user=")
3630
+
3631
+    must_have_one_flag=()
3632
+    must_have_one_noun=()
3633
+}
3634
+
3536 3635
 _openshift_admin_ca()
3537 3636
 {
3538 3637
     last_command="openshift_admin_ca"
... ...
@@ -3541,6 +3640,8 @@ _openshift_admin_ca()
3541 3541
     commands+=("create-key-pair")
3542 3542
     commands+=("create-server-cert")
3543 3543
     commands+=("create-signer-cert")
3544
+    commands+=("encrypt")
3545
+    commands+=("decrypt")
3544 3546
 
3545 3547
     flags=()
3546 3548
     two_word_flags=()
... ...
@@ -9100,6 +9201,105 @@ _openshift_cli_adm_ca_create-signer-cert()
9100 9100
     must_have_one_noun=()
9101 9101
 }
9102 9102
 
9103
+_openshift_cli_adm_ca_encrypt()
9104
+{
9105
+    last_command="openshift_cli_adm_ca_encrypt"
9106
+    commands=()
9107
+
9108
+    flags=()
9109
+    two_word_flags=()
9110
+    flags_with_completion=()
9111
+    flags_completion=()
9112
+
9113
+    flags+=("--genkey=")
9114
+    flags_with_completion+=("--genkey")
9115
+    flags_completion+=("_filedir")
9116
+    flags+=("--in=")
9117
+    flags_with_completion+=("--in")
9118
+    flags_completion+=("_filedir")
9119
+    flags+=("--key=")
9120
+    flags_with_completion+=("--key")
9121
+    flags_completion+=("_filedir")
9122
+    flags+=("--out=")
9123
+    flags_with_completion+=("--out")
9124
+    flags_completion+=("_filedir")
9125
+    flags+=("--api-version=")
9126
+    flags+=("--certificate-authority=")
9127
+    flags_with_completion+=("--certificate-authority")
9128
+    flags_completion+=("_filedir")
9129
+    flags+=("--client-certificate=")
9130
+    flags_with_completion+=("--client-certificate")
9131
+    flags_completion+=("_filedir")
9132
+    flags+=("--client-key=")
9133
+    flags_with_completion+=("--client-key")
9134
+    flags_completion+=("_filedir")
9135
+    flags+=("--cluster=")
9136
+    flags+=("--config=")
9137
+    flags_with_completion+=("--config")
9138
+    flags_completion+=("_filedir")
9139
+    flags+=("--context=")
9140
+    flags+=("--google-json-key=")
9141
+    flags+=("--insecure-skip-tls-verify")
9142
+    flags+=("--log-flush-frequency=")
9143
+    flags+=("--match-server-version")
9144
+    flags+=("--namespace=")
9145
+    two_word_flags+=("-n")
9146
+    flags+=("--server=")
9147
+    flags+=("--token=")
9148
+    flags+=("--user=")
9149
+
9150
+    must_have_one_flag=()
9151
+    must_have_one_noun=()
9152
+}
9153
+
9154
+_openshift_cli_adm_ca_decrypt()
9155
+{
9156
+    last_command="openshift_cli_adm_ca_decrypt"
9157
+    commands=()
9158
+
9159
+    flags=()
9160
+    two_word_flags=()
9161
+    flags_with_completion=()
9162
+    flags_completion=()
9163
+
9164
+    flags+=("--in=")
9165
+    flags_with_completion+=("--in")
9166
+    flags_completion+=("_filedir")
9167
+    flags+=("--key=")
9168
+    flags_with_completion+=("--key")
9169
+    flags_completion+=("_filedir")
9170
+    flags+=("--out=")
9171
+    flags_with_completion+=("--out")
9172
+    flags_completion+=("_filedir")
9173
+    flags+=("--api-version=")
9174
+    flags+=("--certificate-authority=")
9175
+    flags_with_completion+=("--certificate-authority")
9176
+    flags_completion+=("_filedir")
9177
+    flags+=("--client-certificate=")
9178
+    flags_with_completion+=("--client-certificate")
9179
+    flags_completion+=("_filedir")
9180
+    flags+=("--client-key=")
9181
+    flags_with_completion+=("--client-key")
9182
+    flags_completion+=("_filedir")
9183
+    flags+=("--cluster=")
9184
+    flags+=("--config=")
9185
+    flags_with_completion+=("--config")
9186
+    flags_completion+=("_filedir")
9187
+    flags+=("--context=")
9188
+    flags+=("--google-json-key=")
9189
+    flags+=("--insecure-skip-tls-verify")
9190
+    flags+=("--log-flush-frequency=")
9191
+    flags+=("--match-server-version")
9192
+    flags+=("--namespace=")
9193
+    two_word_flags+=("-n")
9194
+    flags+=("--server=")
9195
+    flags+=("--token=")
9196
+    flags+=("--user=")
9197
+
9198
+    must_have_one_flag=()
9199
+    must_have_one_noun=()
9200
+}
9201
+
9103 9202
 _openshift_cli_adm_ca()
9104 9203
 {
9105 9204
     last_command="openshift_cli_adm_ca"
... ...
@@ -9108,6 +9308,8 @@ _openshift_cli_adm_ca()
9108 9108
     commands+=("create-key-pair")
9109 9109
     commands+=("create-server-cert")
9110 9110
     commands+=("create-signer-cert")
9111
+    commands+=("encrypt")
9112
+    commands+=("decrypt")
9111 9113
 
9112 9114
     flags=()
9113 9115
     two_word_flags=()
... ...
@@ -23,6 +23,40 @@ Output the inputs and dependencies of your builds
23 23
 ====
24 24
 
25 25
 
26
+== oadm ca decrypt
27
+Decrypt data encrypted with "oadm ca encrypt"
28
+
29
+====
30
+
31
+[options="nowrap"]
32
+----
33
+	# Decrypt an encrypted file to a cleartext file:
34
+	$ oadm ca decrypt --key=secret.key --in=secret.encrypted --out=secret.decrypted
35
+	
36
+	# Decrypt from stdin to stdout:
37
+	$ oadm ca decrypt --key=secret.key < secret2.encrypted > secret2.decrypted
38
+
39
+----
40
+====
41
+
42
+
43
+== oadm ca encrypt
44
+Encrypt data with AES-256-CBC encryption
45
+
46
+====
47
+
48
+[options="nowrap"]
49
+----
50
+	# Encrypt the content of secret.txt with a generated key:
51
+	$ oadm ca encrypt --genkey=secret.key --in=secret.txt --out=secret.encrypted
52
+	
53
+	# Encrypt the content of secret2.txt with an existing key:
54
+	$ oadm ca encrypt --key=secret.key < secret2.txt > secret2.encrypted
55
+
56
+----
57
+====
58
+
59
+
26 60
 == oadm config
27 61
 Change configuration files for the client
28 62
 
... ...
@@ -23,6 +23,40 @@ Output the inputs and dependencies of your builds
23 23
 ====
24 24
 
25 25
 
26
+== oc adm ca decrypt
27
+Decrypt data encrypted with "oc adm ca encrypt"
28
+
29
+====
30
+
31
+[options="nowrap"]
32
+----
33
+	# Decrypt an encrypted file to a cleartext file:
34
+	$ oc adm ca decrypt --key=secret.key --in=secret.encrypted --out=secret.decrypted
35
+	
36
+	# Decrypt from stdin to stdout:
37
+	$ oc adm ca decrypt --key=secret.key < secret2.encrypted > secret2.decrypted
38
+
39
+----
40
+====
41
+
42
+
43
+== oc adm ca encrypt
44
+Encrypt data with AES-256-CBC encryption
45
+
46
+====
47
+
48
+[options="nowrap"]
49
+----
50
+	# Encrypt the content of secret.txt with a generated key:
51
+	$ oc adm ca encrypt --genkey=secret.key --in=secret.txt --out=secret.encrypted
52
+	
53
+	# Encrypt the content of secret2.txt with an existing key:
54
+	$ oc adm ca encrypt --key=secret.key < secret2.txt > secret2.encrypted
55
+
56
+----
57
+====
58
+
59
+
26 60
 == oc adm config
27 61
 Change configuration files for the client
28 62
 
... ...
@@ -32,7 +32,7 @@ Administrative Commands
32 32
 Commands for managing a cluster are exposed here. Many administrative
33 33
 actions involve interaction with the command-line client as well.`
34 34
 
35
-func NewCommandAdmin(name, fullName string, out io.Writer) *cobra.Command {
35
+func NewCommandAdmin(name, fullName string, out io.Writer, errout io.Writer) *cobra.Command {
36 36
 	// Main command
37 37
 	cmds := &cobra.Command{
38 38
 		Use:   name,
... ...
@@ -90,7 +90,7 @@ func NewCommandAdmin(name, fullName string, out io.Writer) *cobra.Command {
90 90
 				admin.NewCommandCreateErrorTemplate(f, admin.CreateErrorTemplateCommand, fullName+" "+admin.CreateErrorTemplateCommand, out),
91 91
 				admin.NewCommandOverwriteBootstrapPolicy(admin.OverwriteBootstrapPolicyCommandName, fullName+" "+admin.OverwriteBootstrapPolicyCommandName, fullName+" "+admin.CreateBootstrapPolicyFileCommand, out),
92 92
 				admin.NewCommandNodeConfig(admin.NodeConfigCommandName, fullName+" "+admin.NodeConfigCommandName, out),
93
-				cert.NewCmdCert(cert.CertRecommendedName, fullName+" "+cert.CertRecommendedName, out),
93
+				cert.NewCmdCert(cert.CertRecommendedName, fullName+" "+cert.CertRecommendedName, out, errout),
94 94
 			},
95 95
 		},
96 96
 	}
... ...
@@ -12,7 +12,7 @@ import (
12 12
 const CertRecommendedName = "ca"
13 13
 
14 14
 // NewCmdCert implements the OpenShift cli ca command
15
-func NewCmdCert(name, fullName string, out io.Writer) *cobra.Command {
15
+func NewCmdCert(name, fullName string, out io.Writer, errout io.Writer) *cobra.Command {
16 16
 	// Parent command to which all subcommands are added.
17 17
 	cmds := &cobra.Command{
18 18
 		Use:   name,
... ...
@@ -26,5 +26,8 @@ func NewCmdCert(name, fullName string, out io.Writer) *cobra.Command {
26 26
 	cmds.AddCommand(admin.NewCommandCreateServerCert(admin.CreateServerCertCommandName, fullName+" "+admin.CreateServerCertCommandName, out))
27 27
 	cmds.AddCommand(admin.NewCommandCreateSignerCert(admin.CreateSignerCertCommandName, fullName+" "+admin.CreateSignerCertCommandName, out))
28 28
 
29
+	cmds.AddCommand(admin.NewCommandEncrypt(admin.EncryptCommandName, fullName+" "+admin.EncryptCommandName, out, errout))
30
+	cmds.AddCommand(admin.NewCommandDecrypt(admin.DecryptCommandName, fullName+" "+admin.DecryptCommandName, fullName+" "+admin.EncryptCommandName, out))
31
+
29 32
 	return cmds
30 33
 }
... ...
@@ -143,7 +143,7 @@ func NewCommandCLI(name, fullName string, in io.Reader, out, errout io.Writer) *
143 143
 		{
144 144
 			Message: "Advanced Commands:",
145 145
 			Commands: []*cobra.Command{
146
-				admin.NewCommandAdmin("adm", fullName+" "+"adm", out),
146
+				admin.NewCommandAdmin("adm", fullName+" "+"adm", out, errout),
147 147
 				cmd.NewCmdCreate(fullName, f, out),
148 148
 				cmd.NewCmdReplace(fullName, f, out),
149 149
 				cmd.NewCmdApply(fullName, f, out),
... ...
@@ -65,7 +65,7 @@ func CommandFor(basename string) *cobra.Command {
65 65
 	case "oc", "osc":
66 66
 		cmd = cli.NewCommandCLI(basename, basename, in, out, errout)
67 67
 	case "oadm", "osadm":
68
-		cmd = admin.NewCommandAdmin(basename, basename, out)
68
+		cmd = admin.NewCommandAdmin(basename, basename, out, errout)
69 69
 	case "kubectl":
70 70
 		cmd = cli.NewCmdKubectl(basename, out)
71 71
 	case "kube-apiserver":
... ...
@@ -107,7 +107,7 @@ func NewCommandOpenShift(name string) *cobra.Command {
107 107
 
108 108
 	startAllInOne, _ := start.NewCommandStartAllInOne(name, out)
109 109
 	root.AddCommand(startAllInOne)
110
-	root.AddCommand(admin.NewCommandAdmin("admin", name+" admin", out))
110
+	root.AddCommand(admin.NewCommandAdmin("admin", name+" admin", out, errout))
111 111
 	root.AddCommand(cli.NewCommandCLI("cli", name+" cli", in, out, errout))
112 112
 	root.AddCommand(cli.NewCmdKubectl("kube", out))
113 113
 	root.AddCommand(newExperimentalCommand("ex", name+" ex"))
114 114
new file mode 100644
... ...
@@ -0,0 +1,156 @@
0
+package admin
1
+
2
+import (
3
+	"crypto/x509"
4
+	"errors"
5
+	"fmt"
6
+	"io"
7
+	"io/ioutil"
8
+	"os"
9
+
10
+	"github.com/openshift/origin/pkg/cmd/util"
11
+	"github.com/spf13/cobra"
12
+
13
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
14
+
15
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
16
+	pemutil "github.com/openshift/origin/pkg/cmd/util/pem"
17
+)
18
+
19
+const DecryptCommandName = "decrypt"
20
+
21
+type DecryptOptions struct {
22
+	// EncryptedFile is a file containing an encrypted PEM block.
23
+	EncryptedFile string
24
+	// EncryptedData is a byte slice containing an encrypted PEM block.
25
+	EncryptedData []byte
26
+	// EncryptedReader is used to read an encrypted PEM block if no EncryptedFile or EncryptedData is provided. Cannot be a terminal reader.
27
+	EncryptedReader io.Reader
28
+
29
+	// DecryptedFile is a destination file to write decrypted data to.
30
+	DecryptedFile string
31
+	// DecryptedWriter is used to write decrypted data to if no DecryptedFile is provided
32
+	DecryptedWriter io.Writer
33
+
34
+	// KeyFile is a file containing a PEM block with the password to use to decrypt the data
35
+	KeyFile string
36
+}
37
+
38
+const decryptExample = `	# Decrypt an encrypted file to a cleartext file:
39
+	$ %[1]s --key=secret.key --in=secret.encrypted --out=secret.decrypted
40
+	
41
+	# Decrypt from stdin to stdout:
42
+	$ %[1]s --key=secret.key < secret2.encrypted > secret2.decrypted
43
+`
44
+
45
+func NewCommandDecrypt(commandName string, fullName, encryptFullName string, out io.Writer) *cobra.Command {
46
+	options := &DecryptOptions{
47
+		EncryptedReader: os.Stdin,
48
+		DecryptedWriter: out,
49
+	}
50
+
51
+	cmd := &cobra.Command{
52
+		Use:     commandName,
53
+		Short:   fmt.Sprintf("Decrypt data encrypted with %q", encryptFullName),
54
+		Example: fmt.Sprintf(decryptExample, fullName),
55
+		Run: func(cmd *cobra.Command, args []string) {
56
+			kcmdutil.CheckErr(options.Validate(args))
57
+			kcmdutil.CheckErr(options.Decrypt())
58
+		},
59
+	}
60
+
61
+	flags := cmd.Flags()
62
+
63
+	flags.StringVar(&options.EncryptedFile, "in", options.EncryptedFile, fmt.Sprintf("File containing encrypted data, in the format written by %q.", encryptFullName))
64
+	flags.StringVar(&options.DecryptedFile, "out", options.DecryptedFile, "File to write the decrypted data to. Written to stdout if omitted.")
65
+
66
+	flags.StringVar(&options.KeyFile, "key", options.KeyFile, fmt.Sprintf("The file to read the decrypting key from. Must be a PEM file in the format written by %q.", encryptFullName))
67
+
68
+	// autocompletion hints
69
+	cmd.MarkFlagFilename("in")
70
+	cmd.MarkFlagFilename("out")
71
+	cmd.MarkFlagFilename("key")
72
+
73
+	return cmd
74
+}
75
+
76
+func (o *DecryptOptions) Validate(args []string) error {
77
+	if len(args) != 0 {
78
+		return errors.New("no arguments are supported")
79
+	}
80
+
81
+	if len(o.EncryptedFile) == 0 && len(o.EncryptedData) == 0 && (o.EncryptedReader == nil || util.IsTerminalReader(o.EncryptedReader)) {
82
+		return errors.New("no input data specified")
83
+	}
84
+	if len(o.EncryptedFile) > 0 && len(o.EncryptedData) > 0 {
85
+		return errors.New("cannot specify both an input file and data")
86
+	}
87
+
88
+	if len(o.KeyFile) == 0 {
89
+		return errors.New("no key specified")
90
+	}
91
+
92
+	return nil
93
+}
94
+
95
+func (o *DecryptOptions) Decrypt() error {
96
+	// Get PEM data block
97
+	var data []byte
98
+	switch {
99
+	case len(o.EncryptedFile) > 0:
100
+		if d, err := ioutil.ReadFile(o.EncryptedFile); err != nil {
101
+			return err
102
+		} else {
103
+			data = d
104
+		}
105
+	case len(o.EncryptedData) > 0:
106
+		data = o.EncryptedData
107
+	case o.EncryptedReader != nil && !util.IsTerminalReader(o.EncryptedReader):
108
+		if d, err := ioutil.ReadAll(o.EncryptedReader); err != nil {
109
+			return err
110
+		} else {
111
+			data = d
112
+		}
113
+	}
114
+	if len(data) == 0 {
115
+		return fmt.Errorf("no input data specified")
116
+	}
117
+	dataBlock, ok := pemutil.BlockFromBytes(data, configapi.StringSourceEncryptedBlockType)
118
+	if !ok {
119
+		return fmt.Errorf("input does not contain a valid PEM block of type %q", configapi.StringSourceEncryptedBlockType)
120
+	}
121
+
122
+	// Get password
123
+	keyBlock, ok, err := pemutil.BlockFromFile(o.KeyFile, configapi.StringSourceKeyBlockType)
124
+	if err != nil {
125
+		return err
126
+	}
127
+	if !ok {
128
+		return fmt.Errorf("%s does not contain a valid PEM block of type %q", o.KeyFile, configapi.StringSourceKeyBlockType)
129
+	}
130
+	if len(keyBlock.Bytes) == 0 {
131
+		return fmt.Errorf("%s does not contain a key", o.KeyFile)
132
+	}
133
+	password := keyBlock.Bytes
134
+
135
+	// Decrypt
136
+	plaintext, err := x509.DecryptPEMBlock(dataBlock, password)
137
+	if err != nil {
138
+		return err
139
+	}
140
+
141
+	// Write decrypted data
142
+	switch {
143
+	case len(o.DecryptedFile) > 0:
144
+		if err := ioutil.WriteFile(o.DecryptedFile, plaintext, os.FileMode(0600)); err != nil {
145
+			return err
146
+		}
147
+	case o.DecryptedWriter != nil:
148
+		fmt.Fprint(o.DecryptedWriter, string(plaintext))
149
+		if util.IsTerminalWriter(o.DecryptedWriter) {
150
+			fmt.Fprintln(o.DecryptedWriter)
151
+		}
152
+	}
153
+
154
+	return nil
155
+}
0 156
new file mode 100644
... ...
@@ -0,0 +1,206 @@
0
+package admin
1
+
2
+import (
3
+	"crypto/rand"
4
+	"crypto/x509"
5
+	"encoding/pem"
6
+	"errors"
7
+	"fmt"
8
+	"io"
9
+	"io/ioutil"
10
+	"os"
11
+	"unicode"
12
+	"unicode/utf8"
13
+
14
+	"github.com/spf13/cobra"
15
+
16
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
17
+
18
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
19
+	"github.com/openshift/origin/pkg/cmd/util"
20
+	pemutil "github.com/openshift/origin/pkg/cmd/util/pem"
21
+)
22
+
23
+const EncryptCommandName = "encrypt"
24
+
25
+type EncryptOptions struct {
26
+	// CleartextFile contains cleartext data to encrypt.
27
+	CleartextFile string
28
+	// CleartextData is cleartext data to encrypt.
29
+	CleartextData []byte
30
+	// CleartextReader reads cleartext data to encrypt if CleartextReader and CleartextFile are unspecified.
31
+	CleartextReader io.Reader
32
+
33
+	// EncryptedFile has encrypted data written to it.
34
+	EncryptedFile string
35
+	// EncryptedWriter has encrypted data written to it if EncryptedFile is unspecified.
36
+	EncryptedWriter io.Writer
37
+
38
+	// KeyFile contains the password in PEM format (as previously written by GenKeyFile)
39
+	KeyFile string
40
+	// GenKeyFile indicates a key should be generated and written
41
+	GenKeyFile string
42
+
43
+	// PromptWriter is used to write status and prompt messages
44
+	PromptWriter io.Writer
45
+}
46
+
47
+const encryptExample = `	# Encrypt the content of secret.txt with a generated key:
48
+	$ %[1]s --genkey=secret.key --in=secret.txt --out=secret.encrypted
49
+	
50
+	# Encrypt the content of secret2.txt with an existing key:
51
+	$ %[1]s --key=secret.key < secret2.txt > secret2.encrypted
52
+`
53
+
54
+func NewCommandEncrypt(commandName string, fullName string, out io.Writer, errout io.Writer) *cobra.Command {
55
+	options := &EncryptOptions{
56
+		CleartextReader: os.Stdin,
57
+		EncryptedWriter: out,
58
+		PromptWriter:    errout,
59
+	}
60
+
61
+	cmd := &cobra.Command{
62
+		Use:     commandName,
63
+		Short:   "Encrypt data with AES-256-CBC encryption",
64
+		Example: fmt.Sprintf(encryptExample, fullName),
65
+		Run: func(cmd *cobra.Command, args []string) {
66
+			kcmdutil.CheckErr(options.Validate(args))
67
+			kcmdutil.CheckErr(options.Encrypt())
68
+		},
69
+	}
70
+
71
+	flags := cmd.Flags()
72
+
73
+	flags.StringVar(&options.CleartextFile, "in", options.CleartextFile, "File containing the data to encrypt. Read from stdin if omitted.")
74
+	flags.StringVar(&options.EncryptedFile, "out", options.EncryptedFile, "File to write the encrypted data to. Written to stdout if omitted.")
75
+
76
+	flags.StringVar(&options.KeyFile, "key", options.KeyFile, "File containing the encrypting key from in the format written by --genkey.")
77
+	flags.StringVar(&options.GenKeyFile, "genkey", options.GenKeyFile, "File to write a randomly generated key to.")
78
+
79
+	// autocompletion hints
80
+	cmd.MarkFlagFilename("in")
81
+	cmd.MarkFlagFilename("out")
82
+	cmd.MarkFlagFilename("key")
83
+	cmd.MarkFlagFilename("genkey")
84
+
85
+	return cmd
86
+}
87
+
88
+func (o *EncryptOptions) Validate(args []string) error {
89
+	if len(args) != 0 {
90
+		return errors.New("no arguments are supported")
91
+	}
92
+
93
+	if len(o.CleartextFile) == 0 && len(o.CleartextData) == 0 && o.CleartextReader == nil {
94
+		return errors.New("an input file, data, or reader is required")
95
+	}
96
+	if len(o.CleartextFile) > 0 && len(o.CleartextData) > 0 {
97
+		return errors.New("cannot specify both an input file and data")
98
+	}
99
+
100
+	if len(o.EncryptedFile) == 0 && o.EncryptedWriter == nil {
101
+		return errors.New("an output file or writer is required")
102
+	}
103
+
104
+	if len(o.GenKeyFile) > 0 && len(o.KeyFile) > 0 {
105
+		return errors.New("either --genkey or --key may be specified, not both")
106
+	}
107
+	if len(o.GenKeyFile) == 0 && len(o.KeyFile) == 0 {
108
+		return errors.New("--genkey or --key is required")
109
+	}
110
+
111
+	return nil
112
+}
113
+
114
+func (o *EncryptOptions) Encrypt() error {
115
+	// Get data
116
+	var data []byte
117
+	var warnWhitespace = true
118
+	switch {
119
+	case len(o.CleartextFile) > 0:
120
+		if d, err := ioutil.ReadFile(o.CleartextFile); err != nil {
121
+			return err
122
+		} else {
123
+			data = d
124
+		}
125
+	case len(o.CleartextData) > 0:
126
+		// Don't warn in cases where we're explicitly being given the data to use
127
+		warnWhitespace = false
128
+		data = o.CleartextData
129
+	case o.CleartextReader != nil && util.IsTerminalReader(o.CleartextReader) && o.PromptWriter != nil:
130
+		// Read a single line from stdin with prompting
131
+		data = []byte(util.PromptForString(o.CleartextReader, o.PromptWriter, "Data to encrypt: "))
132
+	case o.CleartextReader != nil:
133
+		// Read data from stdin without prompting (allows binary data and piping)
134
+		if d, err := ioutil.ReadAll(o.CleartextReader); err != nil {
135
+			return err
136
+		} else {
137
+			data = d
138
+		}
139
+	}
140
+	if warnWhitespace && (o.PromptWriter != nil) && (len(data) > 0) {
141
+		r1, _ := utf8.DecodeRune(data)
142
+		r2, _ := utf8.DecodeLastRune(data)
143
+		if unicode.IsSpace(r1) || unicode.IsSpace(r2) {
144
+			fmt.Fprintln(o.PromptWriter, "Warning: Data includes leading or trailing whitespace, which will be included in the encrypted value")
145
+		}
146
+	}
147
+
148
+	// Get key
149
+	var key []byte
150
+	switch {
151
+	case len(o.KeyFile) > 0:
152
+		if block, ok, err := pemutil.BlockFromFile(o.KeyFile, configapi.StringSourceKeyBlockType); err != nil {
153
+			return err
154
+		} else if !ok {
155
+			return fmt.Errorf("%s does not contain a valid PEM block of type %q", o.KeyFile, configapi.StringSourceKeyBlockType)
156
+		} else if len(block.Bytes) == 0 {
157
+			return fmt.Errorf("%s does not contain a key", o.KeyFile)
158
+		} else {
159
+			key = block.Bytes
160
+		}
161
+	case len(o.GenKeyFile) > 0:
162
+		key = make([]byte, 32)
163
+		if _, err := rand.Read(key); err != nil {
164
+			return err
165
+		}
166
+	}
167
+	if len(key) == 0 {
168
+		return errors.New("--genkey or --key is required")
169
+	}
170
+
171
+	// Encrypt
172
+	dataBlock, err := x509.EncryptPEMBlock(rand.Reader, configapi.StringSourceEncryptedBlockType, data, key, x509.PEMCipherAES256)
173
+	if err != nil {
174
+		return err
175
+	}
176
+
177
+	// Write data
178
+	if len(o.EncryptedFile) > 0 {
179
+		if err := pemutil.BlockToFile(o.EncryptedFile, dataBlock, os.FileMode(0644)); err != nil {
180
+			return err
181
+		}
182
+	} else if o.EncryptedWriter != nil {
183
+		encryptedBytes, err := pemutil.BlockToBytes(dataBlock)
184
+		if err != nil {
185
+			return err
186
+		}
187
+		n, err := o.EncryptedWriter.Write(encryptedBytes)
188
+		if err != nil {
189
+			return err
190
+		}
191
+		if n != len(encryptedBytes) {
192
+			return fmt.Errorf("could not completely write encrypted data")
193
+		}
194
+	}
195
+
196
+	// Write key
197
+	if len(o.GenKeyFile) > 0 {
198
+		keyBlock := &pem.Block{Bytes: key, Type: configapi.StringSourceKeyBlockType}
199
+		if err := pemutil.BlockToFile(o.GenKeyFile, keyBlock, os.FileMode(0600)); err != nil {
200
+			return err
201
+		}
202
+	}
203
+
204
+	return nil
205
+}
... ...
@@ -870,6 +870,13 @@ type AssetExtensionsConfig struct {
870 870
 	HTML5Mode bool
871 871
 }
872 872
 
873
+const (
874
+	// StringSourceEncryptedBlockType is the PEM block type used to store an encrypted string
875
+	StringSourceEncryptedBlockType = "ENCRYPTED STRING"
876
+	// StringSourceKeyBlockType is the PEM block type used to store an encrypting key
877
+	StringSourceKeyBlockType = "ENCRYPTING KEY"
878
+)
879
+
873 880
 type LDAPSyncConfig struct {
874 881
 	unversioned.TypeMeta
875 882
 
876 883
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+package pem
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/pem"
5
+	"io/ioutil"
6
+	"os"
7
+	"path/filepath"
8
+)
9
+
10
+func BlockFromFile(path string, blockType string) (*pem.Block, bool, error) {
11
+	data, err := ioutil.ReadFile(path)
12
+	if err != nil {
13
+		return nil, false, err
14
+	}
15
+	block, ok := BlockFromBytes(data, blockType)
16
+	return block, ok, nil
17
+}
18
+
19
+func BlockFromBytes(data []byte, blockType string) (*pem.Block, bool) {
20
+	for {
21
+		block, remaining := pem.Decode(data)
22
+		if block == nil {
23
+			return nil, false
24
+		}
25
+		if block.Type == blockType {
26
+			return block, true
27
+		}
28
+		data = remaining
29
+	}
30
+}
31
+
32
+func BlockToFile(path string, block *pem.Block, mode os.FileMode) error {
33
+	b, err := BlockToBytes(block)
34
+	if err != nil {
35
+		return err
36
+	}
37
+	if err := os.MkdirAll(filepath.Dir(path), os.FileMode(0755)); err != nil {
38
+		return err
39
+	}
40
+	return ioutil.WriteFile(path, b, mode)
41
+}
42
+
43
+func BlockToBytes(block *pem.Block) ([]byte, error) {
44
+	b := bytes.Buffer{}
45
+	if err := pem.Encode(&b, block); err != nil {
46
+		return nil, err
47
+	}
48
+	return b.Bytes(), nil
49
+}
... ...
@@ -109,6 +109,12 @@ func readInputFromReader(r io.Reader) string {
109 109
 	return result
110 110
 }
111 111
 
112
+// IsTerminalReader returns whether the passed io.Reader is a terminal or not
113
+func IsTerminalReader(r io.Reader) bool {
114
+	file, ok := r.(*os.File)
115
+	return ok && term.IsTerminal(file.Fd())
116
+}
117
+
112 118
 // IsTerminalWriter returns whether the passed io.Writer is a terminal or not
113 119
 func IsTerminalWriter(w io.Writer) bool {
114 120
 	file, ok := w.(*os.File)
... ...
@@ -71,6 +71,29 @@ os::cmd::expect_failure_and_text 'oadm ca create-master-certs --hostnames=exampl
71 71
 os::cmd::expect_failure_and_text 'oadm ca create-master-certs --hostnames=example.com --master=example.com'                                     'master must be a valid URL'
72 72
 os::cmd::expect_failure_and_text 'oadm ca create-master-certs --hostnames=example.com --master=https://example.com --public-master=example.com' 'public master must be a valid URL'
73 73
 
74
+# check encrypt/decrypt of plain text
75
+os::cmd::expect_success          'echo -n "secret data 1" | oadm ca encrypt --genkey=secret.key --out=secret.encrypted'
76
+os::cmd::expect_success_and_text 'oadm ca decrypt --in=secret.encrypted --key=secret.key' '^secret data 1$'
77
+# create a file with trailing whitespace
78
+echo "data with newline" > secret.whitespace.data
79
+os::cmd::expect_success_and_text 'oadm ca encrypt --key=secret.key --in=secret.whitespace.data      --out=secret.whitespace.encrypted' 'Warning.*whitespace'
80
+os::cmd::expect_success          'oadm ca decrypt --key=secret.key --in=secret.whitespace.encrypted --out=secret.whitespace.decrypted'
81
+os::cmd::expect_success          'diff secret.whitespace.data secret.whitespace.decrypted'
82
+# create a binary file
83
+echo "hello" | gzip > secret.data
84
+# encrypt using file and pipe input/output
85
+os::cmd::expect_success 'oadm ca encrypt --key=secret.key --in=secret.data --out=secret.file-in-file-out.encrypted'
86
+os::cmd::expect_success 'oadm ca encrypt --key=secret.key --in=secret.data     > secret.file-in-pipe-out.encrypted'
87
+os::cmd::expect_success 'oadm ca encrypt --key=secret.key    < secret.data     > secret.pipe-in-pipe-out.encrypted'
88
+# decrypt using all three methods
89
+os::cmd::expect_success 'oadm ca decrypt --key=secret.key --in=secret.file-in-file-out.encrypted --out=secret.file-in-file-out.decrypted'
90
+os::cmd::expect_success 'oadm ca decrypt --key=secret.key --in=secret.file-in-pipe-out.encrypted     > secret.file-in-pipe-out.decrypted'
91
+os::cmd::expect_success 'oadm ca decrypt --key=secret.key    < secret.pipe-in-pipe-out.encrypted     > secret.pipe-in-pipe-out.decrypted'
92
+# verify lossless roundtrip
93
+os::cmd::expect_success 'diff secret.data secret.file-in-file-out.decrypted'
94
+os::cmd::expect_success 'diff secret.data secret.file-in-pipe-out.decrypted'
95
+os::cmd::expect_success 'diff secret.data secret.pipe-in-pipe-out.decrypted'
96
+
74 97
 os::cmd::expect_success 'oc create -f examples/hello-openshift/hello-pod.json'
75 98
 # os::cmd::expect_success_and_text 'oadm manage-node --list-pods' 'hello-openshift'
76 99
 # os::cmd::expect_success_and_text 'oadm manage-node --list-pods' '(unassigned|assigned)'
... ...
@@ -62,6 +62,6 @@ func main() {
62 62
 	oc.GenBashCompletionFile(outFile_osc)
63 63
 
64 64
 	outFile_osadm := outDir + "oadm"
65
-	oadm := admin.NewCommandAdmin("oadm", "openshift admin", ioutil.Discard)
65
+	oadm := admin.NewCommandAdmin("oadm", "openshift admin", ioutil.Discard, ioutil.Discard)
66 66
 	oadm.GenBashCompletionFile(outFile_osadm)
67 67
 }
... ...
@@ -51,6 +51,6 @@ func main() {
51 51
 	gendocs.GenDocs(cmd, outFile)
52 52
 
53 53
 	outFile = outDir + "oadm_by_example_content.adoc"
54
-	cmd = admin.NewCommandAdmin("oadm", "oadm", ioutil.Discard)
54
+	cmd = admin.NewCommandAdmin("oadm", "oadm", ioutil.Discard, ioutil.Discard)
55 55
 	gendocs.GenDocs(cmd, outFile)
56 56
 }